document.getElementbyId can cause big problems in production.
This function makes an
HTMLInputElement, and depending on the
type, it sets the checkboxes
checked attribute to the (supposedly) boolean
value argument. Otherwise it will assign the
value argument to the
value attribute of the input.
There’s a possible, subtle bug here. Consider what happens if you assign the wrong type of value when making a checkbox input:
Even with an accurate JSDoc comment, this bug is not prevented. Setting the checked value argument to the string
"false" will actually make
createInput return an input in checked state — because the string
"false" is evaluated as a boolean
With a type system we can declare function overloads that help developers write correct code from the get-go:
Type systems make discovering code capabilities much easier. They also prevent bugs very early on by hinting at API usages semantics.
Type systems make refactoring easier by giving developers confidence about their changes. For example, when a utility function signature changes, the TypeScript compiler will not allow compilation until all call-sites are corrected with the new signature.
Moreover, strongly typed programs are easier to refactor because the type-checker ensures your changes are compatible with other parts of the project. IDEs and code editors can provide refactoring features backed by the type system.
Type systems also help with the maintainability by distributing modules with type information. Modules written in TypeScript can enable some editors to provide very helpful API usage hints.
TypeScript vs. FlowType
- Google Closure Compiler with JSDoc type annotations
We ultimately decided to use TypeScript, but it wasn’t an easy decision. Our team was evenly divided between those who preferred FlowType versus those who preferred TypeScript.
There were many arguments on both sides, and it’s helpful to consider them in detail.
Call-Site Type Checking
One of much-hailed features of FlowType is call-site based type checking for function calls. Consider this erroneous code:
FlowType will — correctly — warn about passing a string to a function that clearly requires a number as its sole argument. TypeScript will consider a to be any type and compile the above code without errors.
This feature looks impressive but it falls short as soon as we complicate our function even slightly:
This code will get compiled in FlowType without any errors. Call-site type checking is impressive but casts a false sense of security when burdened with the codebase of real-world applications.
Since React and FlowType are both open source projects from Facebook, it’s often assumed that FlowType works better than TypeScript with React. But with projects at Lyft, we didn’t find any meaningful difference when using either with React. React type definitions in DefinitelyTyped are very accurate and helpful.
Even if you consider both options to be equally suitable, for future ecosystem growth it’s critical to consider a project’s popularity. Using a popular option will help Lyft attract more talent while ensuring broader access to other open source projects written in that language.
Though measuring the popularity of an open source project can be a subjective art, I tried my best to find reasonable metrics as a basis for comparison:
Number of external type definitions
We even sent out a survey to our own engineers to investigate internal popularity of each project.. Mirroring the external numbers I collected, we discovered that TypeScript was also more popular internally.
Migrating to TypeScript
So instead, we took the incremental approach. We are using Webpack to compile our frontend applications. It’s painless to introduce TypeScript to Webpack via TypeScript-loader. Once TypeScript-loader is added to the Webpack configuration of a project, we were able to write new files in TypeScript and update our codebase piecemeal.
Writing with TypeScript at Lyft
Since we adopted TypeScript, many new Lyft projects are TypeScript-only. These are just a few samples of projects where we’ve adopted TypeScript.
React has a runtime-based type system for its components: PropTypes, a powerful tool for enforcing interface of shared components. We use PropTypes extensively at Lyft for our non-TypeScript repositories.
In this project we tried to solve the problem of rendering dynamically loaded components on the server-side. It’s written entirely in TypeScript, and represents a good example of how to write shared code with TypeScript by leveraging shared type definitions.
Our CSS framework TypeScript Integration
At Lyft, we use an internal atomic CSS library called Tetris. The methodology behind atomic CSS is couched in the idea that repeating class names is usually cheaper than repeating CSS code. Atomic CSS frameworks introduce many small CSS classes that can be combined to style an element. When using Tetris, developers must remember countless CSS class names (e.g., like p-a-m, which adds padding around a box).
To enhance our developers’ productivity — and reduce the need for perfect memory — we defined a single TypeScript file that exports every Tetris class name. Now Lyft developers can import this file and use their favorite editor’s autocomplete feature to get hints for class names:
Swagger to TypeScript Code Generation
One of the most intractable categories of bugs are those that frequently appear when working with backend APIs. A backend change can break frontend code — or sometimes frontend code changes break compatibility with backend API schemas.
Our backend APIs are described with OpenAPI (Swagger 2.0). Creating a formal specification in this manner gives us the perfect opportunity for strongly typing our API usage in frontend applications. We use Swagger JSON Schema models to automatically generate TypeScript interfaces for our API clients.
Want to work with TypeScript at Lyft?