TypeScript for JavaScript Developers
After resisting TypeScript for two years, I finally made the switch. The catalyst? A production bug that would have been caught instantly with types. Here's how to transition without losing your mind.
Why TypeScript Matters Now
Most companies in India—from Swiggy to Zerodha—have moved their codebases to TypeScript. Job postings increasingly list it as required, not preferred.
Basic Types
// Primitive types
let name: string = "Rahul";
let age: number = 25;
let isActive: boolean = true;
// Arrays
let scores: number[] = [85, 90, 78];
let names: Array<string> = ["Amit", "Priya"];
// Objects
interface User {
id: number;
name: string;
email: string;
phone?: string; // Optional
}
const user: User = {
id: 1,
name: "Priya Sharma",
email: "[email protected]"
};
Functions with Types
// Parameters and return type
function calculateTax(amount: number, rate: number = 0.18): number {
return amount * rate;
}
// Arrow functions
const formatCurrency = (amount: number): string => {
return `₹${amount.toLocaleString('en-IN')}`;
};
// Void return
function logMessage(message: string): void {
console.log(message);
}
Interfaces vs Types
Both work, but here's when to use each:
// Interface - for object shapes, can extend
interface Product {
id: number;
name: string;
price: number;
}
interface DigitalProduct extends Product {
downloadUrl: string;
}
// Type - for unions, primitives, or tuples
type Status = "pending" | "completed" | "failed";
type Coordinates = [number, number];
type Handler = (event: Event) => void;
Generics
The concept that makes TypeScript powerful:
// Generic function
function getFirst<T>(array: T[]): T | undefined {
return array[0];
}
const firstNumber = getFirst([1, 2, 3]); // type: number
const firstName = getFirst(["a", "b"]); // type: string
// Generic interface
interface ApiResponse<T> {
data: T;
status: number;
message: string;
}
const userResponse: ApiResponse<User> = {
data: { id: 1, name: "Test", email: "[email protected]" },
status: 200,
message: "Success"
};
Working with APIs
interface Post {
id: number;
title: string;
content: string;
authorId: number;
}
async function fetchPosts(): Promise<Post[]> {
const response = await fetch("/api/posts");
if (!response.ok) {
throw new Error("Failed to fetch posts");
}
return response.json();
}
// Usage with error handling
try {
const posts = await fetchPosts();
posts.forEach(post => console.log(post.title));
} catch (error) {
if (error instanceof Error) {
console.error(error.message);
}
}
Migrating an Existing Project
- Install TypeScript:
npm install -D typescript @types/node - Create tsconfig.json:
npx tsc --init - Rename files gradually:
.js→.ts,.jsx→.tsx - Fix errors file by file
Start with strict: false in tsconfig, then enable strict mode gradually:
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true
}
}
Common Pitfalls
- Any everywhere - Defeats the purpose. Use
unknownfor truly unknown types. - Type assertions -
as Typeshould be rare, not default. - Ignoring compiler errors - Those red squiggles are catching bugs.
TypeScript has a learning curve, but after a month of consistent use, you'll wonder how you wrote JavaScript without it.