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.

What You'll Learn

💡 Real-World Analogy

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.

Common Errors (and the fix)

Pro Tips

📚 Keep Learning

Frequently Asked Questions

Mini-Challenge: A Strict-Safe Config Parser

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.

🎉 Lesson Complete

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.

Continue this course

Related lessons