Typescript Foundations / Best Practices and Real World Patterns

Refactoring JS codebases to TS

Migrating a JavaScript project to TypeScript can dramatically improve type safety, developer experience, and maintainability. Here's a step-by-step guide to refactor your JS codebase to TS.


🪜 Step-by-Step Migration Plan

✅ Initialize TypeScript

Add TypeScript to your project:

npm install --save-dev typescript
npx tsc --init

This generates a tsconfig.json file. Start with:

{
  "compilerOptions": {
    "target": "ES6",
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true,
    "allowJs": true,
    "checkJs": false,
    "outDir": "./dist",
    "rootDir": "./src",
    "skipLibCheck": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}

📦 Rename Files

Gradually rename .js files to .ts (or .jsx → .tsx for React):

mv src/index.js src/index.ts

Start small: maybe one folder or module.


👮 Fix Obvious Type Errors

Once you rename a file, TypeScript will highlight type issues.

Start by:

  • Adding type annotations to function arguments and return values

  • Defining interfaces or type aliases

  • Installing types for dependencies:

    npm i -D @types/lodash @types/express @types/react

🧱 Add Type Definitions Gradually

Use any temporarily if you're unsure, then tighten types later.

function getUser(id: any): any {
  return fetch(`/api/user/${id}`).then(res => res.json());
}

Later replace any with proper types.


🧪 Add JSDoc (Optional Pre-step)

If you're not ready to rename files, add JSDoc for type-checking in JS files:

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

Enable checkJs: true in tsconfig.json to enforce types in JS files.


⚙️ Gradual Type Coverage

Tools like ts-migrate or ts-prune help in large codebases.

Use typescript with tsc --noEmit to just type-check without building.


📁 Refactor to Use Types Fully

Replace:

  • module.exports → export / export default

  • require() → import

  • Loose objects → Interfaces/Types

// Before
const user = { name: "Tisha", age: 24 };

// After
interface User {
  name: string;
  age: number;
}
const user: User = { name: "Tisha", age: 24 };

🔄 Migration Strategy

Strategy

Description

Big Bang

Convert entire project at once (works for small projects)

Incremental

Convert file by file, gradually increasing type safety

Hybrid (JSDoc)

Use JSDoc to document types before full conversion


🧠 Best Practices

  • Keep strict: true in tsconfig.json for maximum safety

  • Use unknown instead of any when appropriate

  • Avoid declare var unless absolutely needed

  • Prefer interfaces for objects, types for unions or complex compositions

  • Use utility types like Partial, Pick, Omit to reduce redundancy