-
Section 1: Getting Started
-
Section 2: Basic Types
-
Section 3: Functions and Objects
-
Section 4: Advanced Types
-
Section 5: Object-Oriented TypeScript
-
Section 6: Working with Modules
-
Section 7: Tooling and Practice
-
Section 8: Type-Level Programming
-
Section 9: TypeScript with Backends
-
Section 10: Testing Typed Code
-
Section 11: Data Validation with Types
-
Section 12: Practical Projects
-
Section 13: Compiler Internals
-
Section 14: Configuration Deep Dive
-
Section 15: Enums, Symbols, and Special Types
-
Section 16: Working with Async Code
-
Section 17: TypeScript and the DOM
-
Section 18: Advanced Generics Practice
-
Section 19: Working with Third-Party Types
-
Section 20: Monorepo and Large-Scale Practices
-
Section 21: Common Pitfalls and Best Practices
-
Section 22: Interview Practice
-
Section 23: Handbook: Narrowing In Depth
-
Section 24: Handbook: Object Types In Depth
-
Section 25: Handbook: Classes In Depth
-
Section 26: Handbook: Modules In Depth
-
Section 27: Handbook: Declaration Files In Depth
-
Section 28: JSX and Namespaces
-
Section 29: Compiler Configuration Reference
-
Section 30: More Practice Exercises
-
Section 31: Handbook: Everyday Types Deep Dive
-
Section 32: Utility Types Full Reference
-
Section 33: Decorators Reference
-
Section 34: Mixins and Advanced OOP Patterns
-
Section 35: Iterators and Generators Typing
-
Section 36: More Type-Level Programming Practice
-
Section 37: TypeScript Ecosystem Tools
-
Section 38: TypeScript with Testing Frameworks
-
Section 39: TypeScript for Library Authors
-
Section 40: More Interview Practice
-
Section 41: Handbook: Functions In Depth
-
Section 42: Handbook: Type Manipulation Deep Dive
-
Section 43: More Real-World Patterns
-
Section 44: TypeScript Release Notes Highlights
-
Section 45: Final Practice and Review
55: Migrating a JavaScript Project to TypeScript
I've seen this happen a dozen times. You have a working JavaScript project, you're feeling ambitious, and you decide to "just switch" to TypeScript. You rename your files to .ts, run the compiler, and suddenly you're staring at 400 red squiggly lines. Panicked, you start adding : any everywhere just to make the errors go away. At that point, you haven't actually migrated to TypeScript—you've just written JavaScript with extra steps and a lot of frustration.
// OrderCalculator.js
export function calculateTotal(price, taxRate) {
return price + (price * taxRate);
}
// main.js
const price = "100"; // Coming from an input field
const tax = 0.05;
console.log(`Total: ${calculateTotal(price, tax)}`);
// Expected: 105
// Actual: "1005" (JavaScript string concatenation madness)
The Silent Failure of JavaScript Addition
In the snippet above, we have a classic JS bug. Because price is a string, the + operator decides it's doing string concatenation instead of addition. The code doesn't crash; it just gives us a wildly wrong answer. If this were a real checkout page, your customers would be very confused.
When you first move this to TypeScript, you might be tempted to just change the extension to .ts. But if you do that without a strategy, you'll find that TypeScript doesn't know if price is supposed to be a string or a number, and it might actually let this slide if your configuration is too loose, or scream at you for any types if it's too strict.
Turning on the Safety Net with tsconfig
The secret to a painless migration isn't renaming files; it's the tsconfig.json. You don't want to fix every single type error in your entire codebase in one afternoon. Instead, you want to coexist.
I always start by creating a tsconfig.json with these specific flags:
{
"compilerOptions": {
"allowJs": true,
"checkJs": false,
"outDir": "./dist",
"target": "ES6",
"module": "CommonJS",
"strict": true
}
}
allowJs: true is the most important part here. It tells TypeScript, "Hey, I have a mix of JS and TS files. Don't panic when you see a .js file; just let it be." I keep checkJs false initially because I don't want the compiler to report errors in my legacy JS files until I'm ready to tackle them.
The Incremental Migration Strategy
Once the project is compiling again, I use a "leaf-to-root" approach. I find the smallest, most independent utility files (the "leaves" of the dependency tree) and rename those to .ts first. In our case, OrderCalculator.js is a perfect candidate.
When I rename it to OrderCalculator.ts, I immediately define the contract:
// OrderCalculator.ts
export function calculateTotal(price: number, taxRate: number): number {
return price + (price * taxRate);
}
Now, here is where the magic happens. When I go back to main.js (which is still JavaScript), TypeScript is still watching. If I have checkJs: true enabled for that specific file via a // @ts-check comment at the top, the editor will immediately highlight calculateTotal(price, tax) and tell me: "Argument of type 'string' is not assignable to parameter of type 'number'."
I've now found the bug without even converting main.js to TypeScript. I just have to fix the data source:
const price = parseFloat("100"); // Now it's a number
This approach prevents the "any-fest" where you just silence the compiler. You migrate the logic first, define the types, and let the compiler lead you to the bugs in the remaining JavaScript code.
📋 Practical Task
Migrating the User Session Manager
You have been handed a small legacy module that manages user sessions. It currently consists of two JavaScript files. Your task is to migrate them to TypeScript using an incremental approach.
Files provided:
// sessionUtils.js
export function formatSessionId(id) {
return `session_${id}`;
}
// sessionManager.js
import { formatSessionId } from './sessionUtils.js';
export function createSession(user) {
const id = Math.floor(Math.random() * 1000);
return {
userId: user.id,
sessionId: formatSessionId(id),
createdAt: new Date()
};
}
Your Objectives:
- Create a
tsconfig.jsonthat allows JavaScript files to exist alongside TypeScript files. - Rename
sessionUtils.jstosessionUtils.tsand add proper type annotations for theidparameter and the return value. - Rename
sessionManager.jstosessionManager.ts. - Create an
interface Userthat ensures theuserobject passed tocreateSessionhas anidof typenumber. - Apply this interface to the
createSessionfunction.
There are no comments for now.