This episode covers the strategy for migrating an existing JavaScript codebase to TypeScript gradually: allowJs, checkJs, JSDoc annotations, controlled use of any, and converting files one by one until strict mode is active.

It's tempting to rewrite a JavaScript codebase from scratch. In practice, that's expensive and risky. A realistic migration runs gradually: TypeScript is adopted while the application keeps working every day. Episode 13 covers how to do it.
The key is mixed mode. TypeScript can process JavaScript and TypeScript files in one project, typing the files already converted while still accepting those that aren't. Type guarantees are raised step by step, not in a leap.
You'll learn to enable allowJs and checkJs, start checking with JSDoc, use any as a temporary bridge, then convert files and tighten options until strict mode is active.
The conversion starts in the tsconfig settings. The allowJs option makes the compiler accept JavaScript files:
{
"compilerOptions": {
"allowJs": true,
"checkJs": false,
"outDir": "dist"
}
}With allowJs: true, .js files are copied and processed along the way, so you can add .ts files in the middle of an old project without waiting for everything to finish. Compilation keeps running, the app keeps being deployed, and the migration proceeds without halting development.
Once stable, enable checking on JavaScript files:
{
"compilerOptions": {
"allowJs": true,
"checkJs": true
}
}With checkJs: true, the compiler starts checking .js files and reports type errors it can detect from the shape of the code. For files not yet ready to be checked, insert a control comment at the top of the file:
// @ts-nocheck
function hitung(a, b) {
return a + b;
}The // @ts-nocheck comment disables checking for one specific file. Conversely, // @ts-check enables checking on .js files where checking is globally off. Both comments give per-file control during the transition.
Before changing extensions, you can type functions with JSDoc:
/**
* Menjumlahkan dua angka.
* @param {number} a
* @param {number} b
* @returns {number}
*/
function tambah(a, b) {
return a + b;
}The @param {number} and @returns {number} declarations add types without changing the file extension. JSDoc takes advantage of checkJs, delivers safety early, and becomes documentation that can be moved automatically when the file is converted.
Not all code can be accurately typed right away. any is a legitimate bridge during migration:
function prosesData(data: any): any {
return data.peta?.map((item: any) => item.nilai);
}The declaration data: any postpones detailed typing until the data structure is understood. The key is control: any only in the file being handled, marked with a comment so it isn't forgotten, and reduced gradually. Temporary any is far better than stopping completely.
rg "\bany\b" src --glob "*.ts"The rg \bany\b command above finds every use of the any keyword in the source. This list becomes a checklist for reducing any until the project is ready for strict mode.
The next step changes file extensions one at a time, starting from the smallest and most isolated files:
git mv src/utils/hitung.js src/utils/hitung.ts
npx tsc --noEmitThe git mv command changes the extension while preserving git history, then npx tsc --noEmit verifies the result. The conversion starts with pure utilities that have no dependencies, moves up to the modules that use those utilities, and finally reaches the top-level components.
Tip
During migration, enable only one strict option at a time. strictNullChecks first, then the others. Small, frequent changes are easier to track than a big overhaul that stalls the whole project.
Episode 13 shows that migrating to TypeScript doesn't have to stop production. With allowJs, checkJs, JSDoc, controlled any, and gradual conversion, an old codebase can be secured while continuing to run normally.
Key takeaways:
allowJs lets the compiler process JavaScript and TypeScript files together.// @ts-nocheck and // @ts-check control checking per file.any bridges files that haven't been converted yet.In the next episode 14 we'll discuss type declarations for external libraries — using @types packages, writing ambient declarations, and understanding module resolution.