📜 ⬆️ ⬇️

TypeScript to Slack

Or how we stopped worrying and learned to trust the compiler




When Brendan Eich created the very first version of JavaScript for Netscape Navigator 2.0 in just ten days, he hardly expected the extent to which the Slack Desktop App would use his invention. We use only the JavaScript code base for a multi-threaded desktop application that constantly interacts with native code and runs under Windows, macOS and Linux.

Managing large JavaScript code bases is not easy. Whenever we pass objects from Chrome's JavaScript browser to Objective-C in passing, we simply need to guarantee that all the pieces are added together to get a callback through another stream on Node.js. In the desktop world, a small error can lead to an application crash. To this end, we implemented TypeScript (a statically typed superset of JavaScript) and quickly realized how to live without excitement and with love for the compiler. And not only we: a survey of developers at Stack Overflow shows that TypeScript is the third most favorite programming technology . Given how quickly static type checking is gaining momentum, we want to share our experience and techniques.

Static analysis comes to the rescue


In the past, we used JSDoc to document the signatures of our functions. We explained in comments the purpose and method of proper use of classes, functions and variables. This method was not without its problems. Looking at the code itself it is difficult to understand how JavaScript promise will be resolved. You will have to trust that the author of the code has documented everything correctly, and the one who later edited the code correctly updated the documentation. In complex systems with countless modules and dependencies, it is very easy to break a function without even opening the file in which it is written.
')
To remedy the situation, we decided to give a chance to static type checking . Static checking does not change the execution of your code — instead, it analyzes the code and tries to calculate the types where possible, alerting the developer before passing the program.

Static type checking understands that Math.random() returns a number that does not contain the toLowerCase() string method.



In order to show intent more clearly, the user of such a type controller can help the system by manually declaring these types — to inform both people and the machine how the program should behave. The code below defines the interface for the “user” object and the method that the user’s age is supposed to get. Static type checking is able to analyze such a code and warn of typical human errors, such as waiting for the presence of a property that may be undefined .



Interestingly, the code does not change at runtime, that is, static type checking does not impose any additional costs on the end user. The above example when executed looks like classic javascript:



A smart type controller increases our confidence in the code, catches common errors before sending a program to production, and allows the code base to better document itself.

Porting Slack Desktop Code Base to TypeScript


We decided to use Microsoft TypeScript, where static analysis is combined with a compiler. Modern JavaScript is a valid typeScript, so you can use TypeScript without changing a single line of code. This allowed us to implement "gradual typing" by turning on the compiler and static analysis at an early stage, without suspending the work on critical bugs or new functions.

In practice, turning on analysis and compiler without changing the code means that TypeScript will immediately try to understand your code. It uses the built-in types and type definitions that are available for third-party dependencies to analyze the flow of code, indicating subtle errors that no one has noticed before. If TypeScript cannot understand your code, it will simply assume a special type called any , and move on.

Our initial plan was to slowly port files one by one, extending standard JavaScript to more specific type definitions where possible — by adding interfaces, defining class methods as private or public, and declaring enums. Along the way, we made two unexpected discoveries:

First , we were surprised by the number of small bugs found during the conversion code. After talking with other developers who started using type checking, we gladly heard that this happens to everyone: the more code lines a person writes, the more unavoidable he will make a typo in the property, assume the permanent existence of the parent or nested object or use a nonstandard error object.

Secondly , we underestimated how powerful the integration with the editor is. Thanks to the TypeScript linguistic service, auto-completion editors can support context-sensitive programming. TypeScript understands which properties and methods are available for certain objects, so the editor now also understands. The autocompletion system, which prompts only words from the current document, seems barbarous after that. It’s past time when we again searched Google for what events are available for Electron BrowserWindow. There are plugins for Atom , Visual Studio Code , Sublime and almost all other editors. The ability to check the code directly in the editor immediately improved our performance.



Looking into the future and thinking about supporting code, we appreciate the ecosystem around TypeScript. For us as active users of the React and Node / npm ecosystem, the availability of type definitions for third-party libraries is a huge plus. Many of the imported libraries are already compatible with TypeScript. If definitions are not supplied with the module itself, they can probably be found in the fantastic DefinitelyTyped project. For example, React does not come with type definitions, but they are installed with the simple npm install @types/react without the need for further configuration.

TypeScript improved the stability and our mental balance so much that within a few days after the start of the migration we began to use it for all the new code. It took about six months to annotate most JavaScript code for the Slack desktop application.

Commits with confidence


To improve readability and ease of maintenance, all code in the development environment is automatically checked by TSLint before committing , which means that before adding changes to Git, the code is first checked for incorrect expressions according to our rules. We do not allow the use of the implicit any option, that is, in all Slack Desktop code, the type must be explicitly specified if TypeScript cannot automatically determine it.

When it comes time to send changes to the branch, Git first passes the entire code base through the TypeScript compiler, which analyzes all code for structural and functional errors and translates modern functions like async / await into ES2016-compatible code. By the time of opening the pull request, we already have confidence that the code has the right structural dependencies.

This may seem scary.


For us, the advantages of TypeScript dramatically outweigh the disadvantages - which also exist. The most tangible for us were the cost of training. Developers with knowledge of strong typing languages ​​usually mastered the syntax for an hour or two, but a file with all the TypeScript functions can discourage a programmer with only pure JavaScript experience.

The most obvious solution to this problem was the slow roll-out of changes — you can simply use TypeScript without changing any code, add some simple type declarations and leave more complex concepts like inheritance, parameterized types and advanced types (intersection, mapped types) or specific modules, or for a later stage of typing. In the end, experience has shown that even minimal use of TypeScript gives a lot of advantages.

To give back


At Slack, we strive to be decent members of the open source community. In the end, we want to make TypeScript easier for other developers. If we see spaces, we try to fill them.

First of all, Slack's own electron-compile package allows Electron Apps developers to write in TypeScript without worrying about compiling. RxJS, the Reactive Extension library, which is actively used in Slack, Netflix, GitHub and many other companies, has switched to TypeScript with the assistance of Slack. Many small libraries written by our developers gradually get TypeScript support (like spawn-rx , electron-spellchecker , electron-remote , electron-notification-state and electron-windows-notifications ).

To get started with TypeScript, see the official manual . If you want to know how small ported projects look in practice, take a look at the spawn-rx port . If you want to write your first lines on TypeScript for an Electron application, use a great electron-forge , which implements electron-compile and TypeScript support right out of the box - it even comes with an excellent React / TypeScript template, our architecture is very popular with our development team Slack Desktop . If combining modern web technologies with native code for developing cross-platform applications seems like a fascinating business to you, we invite you to work !

Source: https://habr.com/ru/post/326854/


All Articles