Migrate JS to TS Walkthrough
Here are the concrete steps to incrementally migrate an existing JavaScript project to TypeScript.
Step 1: Install and Init
First, install the compiler and generate the configuration file.
npm install --save-dev typescript
npx tsc --init
Step 2: Configure for Coexistence
Open the generated tsconfig.json. The critical flag for incremental migration is allowJs.
{
"compilerOptions": {
"target": "es2022",
"moduleResolution": "node",
"strict": true, // Enforce strict rules on all .ts files
"allowJs": true, // VERY IMPORTANT: Allow importing .js files!
"checkJs": false, // Do not typecheck .js files yet
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src/**/*"]
}
With allowJs: true, your bundler and TS compiler won't crash when a .ts file imports a legacy .js file.
Step 3: Pick a Leaf Node and Rename
Find a utility file with no internal dependencies.
// math.js
export function calculateTotal(price, tax, discount) {
return (price + tax) * (1 - discount);
}
Rename it to math.ts. TypeScript will immediately flag errors because price, tax, and discount implicitly have the type any (which violates strict mode).
Step 4: Fix the Types
Add the explicit types.
// math.ts
export function calculateTotal(price: number, tax: number, discount: number): number {
return (price + tax) * (1 - discount);
}
Now, any other file (even a .js file) that imports calculateTotal will get full IDE autocomplete and type information for this function!
JSDoc as a Stepping Stone
If you cannot rename a file to .ts yet (perhaps due to complex build tooling issues), you can get 80% of TypeScript's benefits directly in JavaScript by using JSDoc comments.
Turn on "checkJs": true in your tsconfig, and write:
/**
* @param {number} price
* @param {number} tax
* @returns {number}
*/
export function calculateTotal(price, tax) { ... }
TypeScript will read these comments and typecheck your JS files as if they were TS!
Rename files one by one starting from the bottom of the dependency tree. Ensure
allowJsis true in your config so the newly typed files can seamlessly interact with the legacy JavaScript codebase.