Migrating JavaScript to TypeScript

Migrating to TypeScript is an incremental process — you enable allowJs , add JSDoc and // @ts-check , rename files .js → .ts one at a time, replace any with real types, and turn on strictness gradually rather than all at once.

Learn Migrating JavaScript to TypeScript in our free TypeScript course — an interactive lesson with runnable examples, a practice exercise and a quick…

Part of the free TypeScript course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

Migrating to TypeScript is like renovating a house while still living in it. You don't demolish everything and move out — you do one room at a time, keeping the place habitable throughout. allowJs lets the old and new rooms share a roof; // @ts-check is putting up safety tape in a room before you fully redo it; flipping strict flags one by one is tightening the building code gradually instead of failing every inspection at once.

1. Start Without Renaming: // @ts-check + JSDoc

You can get type-checking on a plain .js file before renaming anything. Add // @ts-check at the top (or set allowJs + checkJs in tsconfig ), then describe types with JSDoc comments. The editor checks them; the code still runs as ordinary JavaScript because JSDoc is just comments.

2. Rename to .ts & Replace any

Next, rename files .js → .ts from the leaves of your dependency tree upward, so each converted file only imports already-typed code. As you go, replace the implicit any on parameters and data with real interface s and types — and prefer unknown over any for genuinely uncertain values.

3. Turn On Strictness Gradually

Resist setting "strict": true on day one — it can surface thousands of errors at once. Enable flags one at a time : noImplicitAny , then strictNullChecks (usually the biggest payoff), then the rest, finishing with full strict . strictNullChecks in particular forces you to add the null guards that prevent real runtime crashes.

🎯 Your Turn

Add the null guard that strictNullChecks would demand, so firstName(null) returns "anonymous" instead of crashing. Fill in the two blanks marked ___ , then run it.

No blanks this time — just a brief and a starting outline. Migrate the parser to handle missing input safely, run it, and check your output against the example in the comments.

Practice quiz

What is the recommended way to migrate JavaScript to TypeScript?

  • Rewrite the entire codebase at once
  • Delete all .js files first
  • Migrate incrementally, file by file
  • Avoid types entirely

Answer: Migrate incrementally, file by file. Migration is incremental: .js and .ts coexist and you convert files one at a time.

What does the allowJs compiler option do?

  • Lets .js files be part of the TypeScript program
  • Deletes JavaScript files
  • Disables type checking
  • Converts .js to .ts automatically

Answer: Lets .js files be part of the TypeScript program. allowJs lets JavaScript files join the program so they can import and be imported by .ts files.

How can you type a plain .js file without renaming it?

  • You cannot type a .js file
  • Use a .ts extension secretly
  • Add a runtime type library
  • Add // @ts-check and JSDoc comments

Answer: Add // @ts-check and JSDoc comments. // @ts-check plus JSDoc gives editor type-checking on a plain .js file, with no rename.

At runtime, what is JSDoc?

  • Compiled type assertions
  • Just comments, so behaviour is unchanged
  • A separate runtime library
  • Replaced by real types

Answer: Just comments, so behaviour is unchanged. JSDoc is comments; the code runs identically before and after adding it.

Which is a safer placeholder than any during migration?

  • unknown
  • object
  • void
  • never

Answer: unknown. unknown forces you to narrow before use, keeping safety; any throws all checking away.

What is the suggested order for enabling strict flags?

  • Set strict: true on day one
  • Only ever enable strictNullChecks
  • One flag at a time, e.g. noImplicitAny then strictNullChecks
  • Enable them randomly

Answer: One flag at a time, e.g. noImplicitAny then strictNullChecks. Flip flags one at a time so each pull request stays small and reviewable.

What does strictNullChecks force you to do?

  • Remove all null values
  • Handle null and undefined explicitly
  • Use any everywhere
  • Disable optional properties

Answer: Handle null and undefined explicitly. strictNullChecks makes you add guards for null/undefined, preventing real runtime crashes.

In what order should you rename files from .js to .ts?

  • Top-level entry points first
  • Alphabetically
  • Largest files first
  • From the leaves of the dependency tree inward

Answer: From the leaves of the dependency tree inward. Convert leaf files first so each new .ts only imports already-typed code.

Which comment is preferred over // @ts-ignore for a known issue?

  • // @ts-skip
  • // @ts-expect-error
  • // @ts-off
  • // @no-check

Answer: // @ts-expect-error. // @ts-expect-error warns you when the error is finally fixed, unlike // @ts-ignore.

What does noImplicitAny do once enabled?

  • Bans all type annotations
  • Converts any to unknown
  • Flags parameters and values that silently became any
  • Disables JSDoc

Answer: Flags parameters and values that silently became any. noImplicitAny surfaces every spot still missing a type by banning silent any.