How to Transition a JavaScript Project to TypeScript Without Breaking Things

A practical guide to adopting TypeScript gradually and safely

Posted by Hüseyin Sekmenoğlu on March 13, 2024 Frontend Development

JavaScript is flexible and powerful but as your project grows so does the risk of bugs, unclear APIs and messy code. If you've considered switching to TypeScript but feel overwhelmed, you're not alone. The good news is that you do not need to rewrite your entire codebase overnight. TypeScript is designed to be adopted incrementally, giving you control over the pace and scope of the transition.


🧱 Step 1: Prepare Your Project

Before you dive in, make sure your project is ready to support TypeScript.

Install TypeScript as a development dependency:

npm install --save-dev typescript

Create a tsconfig.json file:

npx tsc --init

This file configures how TypeScript should behave. You can customize it later but starting with the default setup is fine.

Add an @types folder for type definitions of third-party libraries:

npm install --save-dev @types/node

📝 Step 2: Rename a File to .ts

Pick a simple, non-critical file and rename it from .js to .ts. This helps you test the waters. TypeScript will immediately start showing type warnings or errors. Most will be related to implicit any types or missing type definitions.

If the file uses JSX (for React), rename it to .tsx instead.


🧹 Step 3: Fix Type Errors Gradually

Once the file is renamed, TypeScript will highlight issues such as:

  • Unknown types

  • Missing imports

  • Unsafe null usage

You can fix these incrementally. Start by annotating function parameters and return types. You can use any temporarily if you're unsure but aim to replace it with stricter types over time.

If you're unsure of a type, let the TypeScript compiler infer it. TypeScript’s inference is usually very accurate.


🪜 Step 4: Enable allowJs and checkJs

In your tsconfig.json, enable these options:

{
  "compilerOptions": {
    "allowJs": true,
    "checkJs": true
  },
  "include": ["src"]
}

This allows you to keep .js files in your project while TypeScript starts checking them. It gives you the benefits of static analysis without renaming everything.

You can even add type comments to JavaScript using JSDoc syntax:

/**
 * @param {string} name
 * @returns {string}
 */
function greet(name) {
  return `Hello ${name}`;
}

⚠️ Step 5: Handle Third-Party Libraries

If your project relies on third-party packages, install type definitions:

npm install --save-dev @types/express

If types are missing and the library has no definition file, you can create a declaration manually:

// declare a module with unknown types
declare module 'some-untypeable-library';

Use this sparingly and revisit later for stricter typing.


🧰 Step 6: Start Using Interfaces and Types

Gradually refactor your existing code to use TypeScript features like:

  • interface for structured objects

  • type aliases for unions or advanced shapes

  • Enums for constants

  • Generics for reusable utilities

You do not have to go full TypeScript from day one. Focus on improving the clarity of the most used and most fragile parts of your application first.


📦 Step 7: Transition Build and Tooling

If your build pipeline includes Webpack, Babel or other bundlers, you may need plugins:

  • ts-loader for Webpack

  • @babel/preset-typescript if you're using Babel

Update scripts in package.json to include:

"scripts": {
  "build": "tsc"
}

Some tools like ESLint may require new configs or plugins such as:

npm install --save-dev @typescript-eslint/parser @typescript-eslint/eslint-plugin

🔒 Step 8: Enable Strict Mode

Once you are comfortable with TypeScript, enable strict mode in tsconfig.json:

{
  "compilerOptions": {
    "strict": true
  }
}

This enforces strict type checking and surfaces issues that might be hiding behind implicit any types or unchecked nulls.


🧭 Recommended Migration Path

  1. Migrate core utility files and config logic

  2. Move to shared modules or services

  3. Migrate major UI components or API handlers

  4. Convert legacy or edge-case files last

  5. Set a deadline to convert all files, then disable allowJs


✅ Benefits of Incremental Adoption

  • Safer refactors with real-time feedback

  • Improved collaboration through clear contracts

  • Better autocomplete and tooling

  • Catch bugs earlier in development

  • Easier onboarding for new team members


📌 Conclusion

Transitioning from JavaScript to TypeScript does not have to be stressful or disruptive. By following an incremental approach, you can adopt TypeScript in a way that fits your project and team. Start small, fix gradually and leverage TypeScript’s powerful type system to build safer and more maintainable code.