Priscila Oliveira and Mark Story recently shared lessons learned when converting Sentry’s frontend codebase (one-year effort, 100,000 lines of code) to TypeScript. The pair described a gradual conversion process in which TypeScript progressively replaced JavaScript, types were continuously refined as new TypeScript language features were released, and complex types are built incrementally.
In a blog post, Oliveira and Story detailed the motivation behind adding types to Sentry’s codebase:
Back in 2019, we were shipping more frontend bugs than what was acceptable. After looking at the underlying causes of these incidents, it became clear that many of these bugs could have been prevented by static analysis and type checking.
After debating the pros and cons, the decision was made to incrementally convert the existing React codebase to TypeScript. The conversion involved over 18 months a dozen engineers with 1.100 JavaScript files converted, aggregating around 100,000 lines of code.
The pros of using TypeScript include the advantages related to using a static type system: detection of a category of errors at compile time; accurate, always synchronized documentation of interfaces; and more. Additional advantages stem from the TypeScript tooling and ecosystem: better developer experience through code editor integration, extensive tooling, a large community, and more. TypeScript importantly supports gradual typing, which allows incremental adoption of types across a codebase.
On the other hand, a TypeScript conversion effort of the reported scale would necessarily involve a large time investment, constantly supporting the impacted teams, and the extra mental gymnastic due to the coexistence of dynamically typed (JavaScript) and statically typed code.
Sentry reported adopting a phased conversion strategy that ensured proper communication and buy-in from the front-end teams and dictated adopting TypeScript for any new code. The latter guaranteed that the number of JavaScript files to convert would necessarily decline over time.
Beyond communication, buy-in, and the provision of educational resources, Sentry also took steps to limit the risk of the conversion project:
Our most controversial decision was agreeing to not undergo any other major refactors until the codebase was converted 100% to TypeScript. This meant we would not take on other quality-of-life improvements — things like upgrading our state-management library or introducing React hooks — until the TypeScript conversion was complete.
Interestingly, new TypeScript releases, which may require extra efforts due to breaking changes, were nonetheless embraced for the new language features they provide (e.g., optional chaining, nullish coalescing, named tuples).
Sentry reported facing interoperability issues between TypeScript and React. TypeScript may not be able to correctly infer types for the default props of a React component when used in conjunction with higher-order components, a pattern in which a component factory creates a React component from a series of parameters. React.forwardRef is not compatible with generics, which led the team to resort to the any
type. A few solutions to the latter issue have been proposed by members of the React community.
Developers may find it interesting to compare Sentry’s approach to that followed by Airbnb, which released last year ts-migrate
, a tool that strives to support the migration from JavaScript to TypeScript. Airbnb’s all-in approach and reliance on automation may be dictated by the size of its codebase (more than 2 million lines of JavaScript code). The Airbnb engineering team reported converting projects with over 50,000 lines of code in one day — albeit manual revision of some annotations (e.g., // @ts-expect-error
, and any
) may still be necessary.
The full blog post is available on Sentry’s website. Developers are encouraged to refer to the post for additional extensive details and illustrations on Sentry’s migration journey to TypeScript.