TypeScript Migration Guide
Migrating from JavaScript to TypeScript can significantly improve your codebase's maintainability and developer experience.
This guide will walk you through the process step by step.
Preparation Phase
Assess Your Codebase
Before starting the migration:
- Identify the size and complexity of your codebase
- Document the build process and dependencies
- Check for any existing type definitions (
.d.ts
files) - Identify critical paths that need special attention
Set Up Version Control
Ensure you have a clean git repository or equivalent:
# Create a new branch for the migration
git checkout -b typescript-migration
# Commit your current state
git add .
git commit -m "Pre-TypeScript migration state"
Configuration
Install TypeScript
# Install TypeScript as a dev dependency
npm install --save-dev typescript @types/node
Create tsconfig.json
Create a basic tsconfig.json
to start with:
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
Note: Adjust the target
based on your minimum supported environments.
Migration Approaches
Gradual Migration
Migrate one file at a time while keeping the rest as JavaScript.
{
"compilerOptions": {
"allowJs": true,
"checkJs": true
}
}
Best for: Large codebases, minimal disruption
All-at-Once Migration
Rename all .js
files to .ts
and fix errors.
# Rename all JS files to TS
find src -name "*.js" -exec sh -c 'mv "$0" "${0%.js}.ts"' {} \;
Best for: Small to medium projects, greenfield projects
Important Note
For large projects, we strongly recommend the gradual migration approach to minimize disruption and make the process more manageable.
Step-by-Step Migration
Start with Configuration
Create a basic tsconfig.json
with these recommended settings:
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"outDir": "./dist",
"rootDir": "./src",
"allowJs": true,
"checkJs": true,
"noEmit": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
Enable Type Checking for JavaScript
Add // @ts-check
to the top of your JavaScript files to enable type checking:
// @ts-check
/** @type {string} */
const name = 'John';
// TypeScript will catch this error
name = 42; // Error: Type '42' is not assignable to type 'string'
Note: You can disable type checking for specific lines using // @ts-ignore
.
Rename Files to .ts
Start with non-critical files and rename them from .js
to .ts
:
# Rename a single file
mv src/utils/helpers.js src/utils/helpers.ts
# Or rename all files in a directory (use with caution)
find src/utils -name "*.js" -exec sh -c 'mv "$0" "${0%.js}.ts"' {} \;
Add Type Annotations
Gradually add type annotations to your code:
// Before
function add(a, b) {
return a + b;
}
// After
function add(a: number, b: number): number {
return a + b;
}
// With interface
interface User {
id: number;
name: string;
email?: string;
}
function getUser(id: number): User {
return { id, name: 'John Doe' };
}
Update Build and Test Scripts
Modify your package.json
to include TypeScript compilation:
{
"scripts": {
"build": "tsc",
"dev": "tsc --watch",
"test": "jest"
}
}
Note: Make sure to update your test configuration to work with TypeScript files.
Migration Tools
ts-migrate
Automated tool for migrating JavaScript to TypeScript
npx ts-migrate-full .
@types Packages
Install type definitions for your dependencies
npm install --save-dev @types/react @types/node
Best Practices for TypeScript Migration
Start Small and Iterate
- Begin with utility functions and non-UI components
- Migrate one file or module at a time
- Commit after each successful migration step
Leverage TypeScript Features
// Use type inference where possible
const name = 'John'; // TypeScript infers 'string'
const age = 30; // TypeScript infers 'number'
// Use union types for flexibility
type Status = 'active' | 'inactive' | 'pending';
// Use type guards for runtime checks
function isString(value: any): value is string {
return typeof value === 'string';
}
Handle Third-Party Libraries
- Install
@types
packages for your dependencies - Create declaration files for libraries without types
- Use
declare module
for global type extensions
Common Challenges and Solutions
Dynamic Properties
Problem: JavaScript often uses objects as dictionaries.
// Before
const user = {};
user.name = 'John'; // Error: Property 'name' does not exist
Solution: Use index signatures or type assertions.
// Option 1: Index signature
interface User {
[key: string]: any;
}
const user: User = {};
user.name = 'John'; // OK
// Option 2: Type assertion
const user = {} as { name: string };
user.name = 'John'; // OK
Handling this
Context
Problem: this
binding issues in callbacks.
class Counter {
count = 0;
increment() {
setTimeout(function() {
this.count++; // Error: 'this' is not defined
}, 1000);
}
}
Solution: Use arrow functions or bind this
.
// Solution 1: Arrow function
setTimeout(() => {
this.count++; // 'this' is lexically scoped
}, 1000);
// Solution 2: Bind 'this'
setTimeout(function(this: Counter) {
this.count++;
}.bind(this), 1000);
Conclusion
Migrating from JavaScript to TypeScript is a significant but rewarding investment in your codebase.
By following this guide, you can make the transition smoothly and incrementally.
Key Takeaways:
- Start with a solid
tsconfig.json
configuration - Use
allowJs
andcheckJs
for gradual migration - Leverage TypeScript's type system to catch errors early
- Update your build and test processes to support TypeScript
- Address common challenges with the patterns shown above
Remember that migration is a process, not an event.
It's okay to have a mixed codebase during the transition period.
The important thing is to keep making progress while maintaining code quality.
Ready to Start Your Migration?
Begin by setting up TypeScript in your project and gradually adding type annotations.
The TypeScript compiler will guide you through the process of making your code more robust and maintainable.
For more information, check out the official TypeScript migration guide.