How do you migrate a large JavaScript repo to TypeScript efficiently?
JavaScript Developer
answer
A large JS-to-TypeScript migration should be incremental: start with JSDoc type annotations, convert non-critical modules gradually, and introduce tsconfig strictness in stages. Manage third-party types with @types packages and temporary any/type overrides. Use project references or isolated builds to optimize compile time. CI pipelines leverage caching, parallelization, and selective checks to keep feedback fast and reliable while ensuring correctness throughout the migration.
Long Answer
Migrating a large JavaScript codebase to TypeScript is challenging due to scale, build performance, and third-party type management. A structured, incremental approach mitigates risk, maintains developer productivity, and preserves CI reliability.
1) Incremental migration and JSDoc
- Begin with JSDoc type annotations in critical modules. This provides type safety in editors and optional type checking without changing runtime behavior.
- Introduce checkJs: true in tsconfig.json to enable gradual verification of JS files.
- Convert isolated folders to .ts or .tsx while keeping the rest in JS.
- Prioritize core business logic modules for early conversion; defer peripheral or low-risk modules.
2) Strictness ramp and tsconfig strategy
- Start with lenient settings: strict: false, noImplicitAny: false, allowJs: true.
- Gradually increase strictness per folder or per module: enable strictNullChecks, noImplicitAny, strictBindCallApply.
- Use project references (composite: true) to divide code into buildable units; enables per-package strictness and incremental builds.
- Maintain a migration branch or flag for in-progress modules to isolate partially converted code.
3) Third-party type management
- Install @types/* for well-maintained DefinitelyTyped packages.
- For untyped packages, provide temporary ambient declarations or use declare module "package" with any.
- Encourage community contributions for missing types or write lightweight minimal types for your code usage.
- Track type drift in CI to prevent any creeping into critical areas.
4) Build performance optimization
- Enable incremental compilation (incremental: true) and tsBuildInfoFile caching.
- Use isolatedModules: true when integrating with Babel or transpilers for faster CI builds.
- Parallelize compilation using project references; only recompile affected packages.
- For frontend repos, combine TypeScript with Babel for transpile-only mode in CI for fast feedback; run full type check nightly or on main branches.
5) CI pipeline considerations
- Split CI into fast path and full path:
- Fast path: lint, unit tests, and incremental type checks on PRs.
- Full path: strict type checks, build verification, and integration tests nightly or pre-merge.
- Fast path: lint, unit tests, and incremental type checks on PRs.
- Cache node_modules, TypeScript build info files, and generated artifacts.
- Use monorepo tooling (Nx, Turborepo, Lerna) for incremental builds and dependency graph awareness.
6) Developer ergonomics and adoption
- Enable editor type hints and error reporting to help developers adopt TypeScript gradually.
- Enforce consistent tsconfig settings per folder; consider eslint @typescript-eslint rules aligned with migration stage.
- Provide migration guides, codemods, and pre-commit hooks for gradual .js → .ts conversion.
7) Testing and verification
- Ensure unit tests cover converted modules; run them in CI alongside type checks.
- Integration tests detect behavioral regressions in partially typed code.
- Use type-aware testing helpers (ts-jest) for TypeScript test coverage.
Summary: Successful JS-to-TypeScript migration at scale relies on phased adoption, JSDoc, staged strictness, type-safe third-party management, and optimized CI. Incremental, observable progress ensures developer productivity, safe type enforcement, and reliable deployments without slowing down feedback loops.
Table
Common Mistake (900–1000 chars)
- Converting entire repo at once → massive merge conflicts, slowed CI.
- Skipping JSDoc/type annotations for gradual adoption → poor editor hints.
- Over-strict tsconfig too early → developer frustration and PR delays.
- Ignoring untyped third-party modules → type errors or accidental any.
- Full type checks on every commit without caching → slow CI feedback.
- Lack of project references → rebuilds affect unrelated modules.
- Not pairing migration with tests → runtime regressions unnoticed.
- No CI path differentiation → PRs block on nightly-length type checks.
Sample Answers
Junior:
“I would start with JSDoc type annotations and checkJs to get basic editor hints. I’d convert key modules to .ts gradually and install @types for dependencies. CI runs fast unit tests and basic type checks per PR.”
Mid:
“I implement incremental migration using JSDoc, then convert core packages to .ts with project references. I ramp tsconfig strictness folder by folder. Third-party types are managed via DefinitelyTyped or ambient declarations. CI has fast-path incremental type checks and nightly full strict checks.”
Senior:
“My approach uses phased conversion: JSDoc first, then core modules to TypeScript with project references. tsconfig strictness is gradually increased. Dependencies with missing types get temporary ambient declarations or lightweight typings. CI/CD uses incremental builds, caching, and fast-path PR checks; nightly full builds enforce strict types. Tests run with ts-jest and integration tests ensure correctness. Rollback is easy if a converted module causes regressions, as PRs are gated by fast CI.”
Evaluation Criteria
Look for:
- Phased migration: JSDoc → .ts conversion → strictness ramp.
- Third-party type management (@types, ambient declarations).
- Build optimization: incremental compilation, project references, caching.
- CI strategy: fast-path for PRs, full build nightly or pre-merge.
- Test coverage: unit, integration, and type-aware tests.
Red flags: converting entire repo at once, full strictness from day one, ignoring third-party types, slow CI pipelines, or lack of test verification.
Preparation Tips
- Annotate JS with JSDoc to allow incremental checking.
- Convert core modules first; use project references for incremental builds.
- Ramp tsconfig strict options gradually.
- Track third-party types; provide temporary ambient declarations for untyped packages.
- Configure CI/CD: fast-path incremental type checks, nightly full builds, caching of node_modules and .tsbuildinfo.
- Integrate ts-jest for type-aware unit tests; add integration tests for converted modules.
- Provide editor tooling: ESLint + TypeScript rules consistent with migration stage.
- Measure CI build times and optimize parallelization and caching to maintain developer velocity.
Real-world Context
- A large React repo migrated incrementally using JSDoc → TypeScript → strictness ramp; developer productivity remained high.
- Project references enabled incremental builds, reducing CI build time from 40 to 12 minutes.
- Unmaintained third-party packages were typed via ambient declarations, preventing type errors while awaiting community types.
- Fast-path CI on PRs allowed feedback within 3–5 minutes, while nightly full strict type checks ensured code quality.
- Integration tests caught regressions in converted modules before merge, reducing runtime errors in production.
This demonstrates that phased TypeScript migration with CI enforcement maintains speed, correctness, and safety at scale.
Key Takeaways
- Migrate incrementally: JSDoc → core .ts modules → strictness ramp.
- Manage third-party types with @types or ambient declarations.
- Use project references and incremental builds to optimize CI performance.
- Split CI into fast-path PR checks and full nightly builds.
- Pair migration with unit and integration tests to prevent runtime regressions.
- Maintain developer velocity while enforcing correctness and type safety.
Practice Exercise
Scenario:
You maintain a 2M+ line JavaScript repo. Management wants gradual TypeScript adoption without slowing CI or disrupting developers.
Tasks:
- Annotate core modules with JSDoc and enable checkJs in tsconfig.
- Convert high-value modules to .ts using project references for incremental compilation.
- Gradually enable strict options (noImplicitAny, strictNullChecks) folder by folder.
- Install @types for dependencies; write ambient declarations for untyped packages.
- Configure CI: fast-path incremental type checks per PR; nightly full strict type build.
- Run ts-jest for unit tests; integration tests for converted modules.
- Cache .tsbuildinfo and node_modules in CI to maintain speed.
- Track build times; optimize pipeline parallelization.
- Document migration plan and assign modules to developers for phased adoption.
Deliverable:
A repo demonstrating incremental TypeScript migration, optimized CI/CD, third-party type management, strictness ramping, and reliable testing infrastructure that preserves build speed and correctness.

