Over two years ago, the Wealthfront web team began adopting TypeScript to take advantage of the type safety and related functionality it enables. This is a common migration in the past few years across the industry since the web community mostly settled on TypeScript as the language of choice.
Since then, the codebase has naturally migrated towards TypeScript as engineers converted the code they touched, and all new code was written in TypeScript. After two years, the codebase was about 35% TypeScript by file count. This was great progress and helped increase the team’s confidence in adopting and using TypeScript. Unfortunately, at this pace, it wouldn’t be complete for four more years.
Gradual migration was happening, but it was being impeded. We set out to remove points of friction to expedite this migration and realize as many of TypeScript’s benefits as soon as possible. In the end, renaming the remaining files removed much of the friction and provided many of the benefits.
The first obstacle we commonly faced was renaming existing files from a JS/JSX extension to a TS/TSX extension. This was simple but required restarting the local development server which can take several minutes.
Second, renaming a file would then introduce countless new type errors, which now had to be fixed. This could be a time-consuming process depending on the complexity of the types. It may also require converting more files, or changing existing types in other files. In turn, this could create more type errors, ending up in a rabbit hole fixing type errors.
The majority of the migration was done with Airbnb’s ts-migrate tool which was built specifically for this use case. It contains several commands to perform different tasks as part of the migration.
First, the tool provides a rename command to convert files from a “JS/JSX” extension to a “TS/TSX” extension. However, doing this will introduce many new type errors.
The next step is to fix (or ignore) all of these type errors so the project successfully compiles. This tool contains a set of plugins that modify the code (codemod) to resolve as many of these errors as possible. Any remaining errors are then ignored so the project successfully compiles.
There were two approaches considered, migrate the codebase all at once, or in iterative chunks.
An all-at-once approach would likely require a code freeze which requires additional coordination but can avoid issues like merge conflicts. Any potential issues become much harder to debug and revert.
Migrating in chunks can be performed on smaller pieces of the codebase which can be more easily coordinated to avoid merge conflicts, reviewed, and merged. If unexpected issues do arise, it’s a significantly smaller search area. For these reasons, the codebase was migrated in several chunks each day over the course of a week.
The first step was to run the
ts-migrate rename command on a subdirectory of files to change the file extension.
ts-migrate migrate command was run on the same subdirectory which runs several plugins. These plugins attempt to fix as many type errors as possible by adding missing class properties for legacy React refs, converting React prop types to TypeScript types, adding explicit
any keywords for missing types, or in the case of some type errors casting the value to
any. Any remaining errors were then ignored with the
After, Prettier was run over these files to format them consistently, which sometimes caused issues that required re-running the
migrate command. Finally, any new ESLint errors also had to be fixed or ignored.
This tool effectively converted the entire codebase to TypeScript files, but as a result, added many
any types and ignored many errors with the
Most of these types and errors require a human to read and understand the code to properly fix.
Further improving type safety
With the entire codebase fully converted to TypeScript, our effort can now be spent incrementally improving type safety. To assist with further improving type safety, a command-line tool was added to aggregate this information to surface opportunities and fix systemic typing problems.
It counts the total number of
any keywords and
@ts-expect-error directives across the entire codebase.
The following code is a simplified example of this tool that iterates through the relevant files and converts the source code into an abstract syntax tree to traverse and find the relevant nodes (
any keywords and
In addition to totals like this example, the command-line tool also aggregates by file name and error message so it’s easier to diagnose and improve systemic type issues.
This same tool can then be run across git history to generate a trend to ensure consistent progress is being made towards improving type safety over time by reducing the total number of
any keywords and
This is not a perfect metric for “type quality”, but given the current state of the codebase, it’s measuring the biggest opportunities for improvement. Once most of these type issues are resolved, a different measure of “type quality” will likely need to be established.
While converting the entire codebase to TypeScript files is only a milestone along the journey of a strongly typed codebase, it’s an important step to maintain and improve the quality of the codebase and product over time. In our case, it proved to be one of the biggest obstacles in the way of the gradual migration towards stronger type safety throughout the web codebase.
The information contained in this communication is provided for general informational purposes only, and should not be construed as investment or tax advice. Nothing in this communication should be construed as a solicitation or offer, or recommendation, to buy or sell any security. Any links provided to other server sites are offered as a matter of convenience and are not intended to imply that Wealthfront Advisers or its affiliates endorses, sponsors, promotes and/or is affiliated with the owners of or participants in those sites, or endorses any information contained on those sites, unless expressly stated otherwise.
All investing involves risk, including the possible loss of money you invest, and past performance does not guarantee future performance. Please see our Full Disclosure for important details.
Wealthfront offers a free software-based financial advice engine that delivers automated financial planning tools to help users achieve better outcomes. Investment management and advisory services are provided by Wealthfront Advisers LLC, an SEC registered investment adviser, and brokerage related products are provided by Wealthfront Brokerage LLC, a member of FINRA/SIPC.
Wealthfront, Wealthfront Advisers and Wealthfront Brokerage are wholly owned subsidiaries of Wealthfront Corporation.
© 2022 Wealthfront Corporation. All rights reserved.