A language tour
JavaScript grew up across thirty years of accumulation. TypeScript is what it learned.
01 — Type Safety
TypeScript adds static typing to JavaScript. It catches type errors before your code runs, making large applications more reliable.
// Variables have types let name: string = "Alice"; let age: number = 30; let isActive: boolean = true; // Functions specify parameter and return types function greet(person: string): string { return `Hello, ${person}!`; } // TypeScript catches errors greet(123); // Error: Argument of type 'number' is not assignable to parameter of type 'string'
Types document your intent and prevent runtime errors — the compiler becomes your first line of defense.
02 — Interfaces
Interfaces define the shape of objects. They create contracts that your code must follow, making APIs self-documenting.
"A type annotation is the most efficient form of documentation — it can never drift from the code it describes."
— The TypeScript design philosophyinterface User { id: number; name: string; email: string; isActive?: boolean; // Optional property } function createUser(userData: User): User { return { id: userData.id, name: userData.name, email: userData.email, isActive: userData.isActive ?? true }; } const user = createUser({ id: 1, name: "Alice", email: "[email protected]" });
Interfaces define contracts — the compiler ensures your data matches the expected shape.
03 — Generics
Generics let you write reusable code that works with any type, while maintaining type safety.
// One wrapper type for every API response interface ApiResponse<T> { data: T; status: number; error: string | null; } // One function handles every endpoint async function fetchJson<T>(url: string): Promise<ApiResponse<T>> { const res = await fetch(url); const data: T = await res.json(); return { data, status: res.status, error: null }; } // TypeScript infers the full type from usage const result = await fetchJson<User[]>('/api/users'); result.data.forEach(u => console.log(u.name)); // u.name: string ✓
The compiler infers T as User[] at the call site — one function, every endpoint, full type safety throughout.
04 — Union Types
Union types let a value be one of several types. They model real-world data that can take different forms.
// Union type type StringOrNumber = string | number; function printId(id: StringOrNumber) { if (typeof id === "string") { console.log(`String ID: ${id.toUpperCase()}`); } else { console.log(`Number ID: ${id}`); } } // Discriminated unions type Success = { type: "success"; data: string }; type Error = { type: "error"; message: string }; type Result = Success | Error; function handleResult(result: Result) { if (result.type === "success") { console.log(result.data); } else { console.error(result.message); } }
Unions model choice — a value can be this OR that, with type-safe handling of each case.
05 — Gradual Adoption
TypeScript is a superset of JavaScript. You can add types gradually — start with any .js file and rename it to .ts.
// Plain JavaScript is valid TypeScript — rename .js to .ts function add(a, b) { return a + b; } // Add types incrementally as you understand each function function multiply(a: number, b: number): number { return a * b; } // 'unknown' for values you can't predict — safer than 'any' let incoming: unknown = parseFromLegacyApi(); if (typeof incoming === "string") { console.log(incoming.toUpperCase()); // narrowed: TypeScript knows it's a string } // Type assertion for DOM elements you know the shape of const input = document.getElementById("email") as HTMLInputElement;
unknown is the honest alternative to any — it forces you to narrow the type before using it, rather than silently bypassing the type system.
06 — The Whole Picture
TypeScript brings honesty to JavaScript. The types describe what's actually true about your data — not what you hoped was true at 2am before a release.
Catches errors at compile time, prevents runtime bugs, and serves as living documentation.
Makes large codebases manageable with interfaces, generics, and structural typing.
Add types incrementally — works with existing JavaScript code and teams.
Excellent IDE support with autocomplete, refactoring, and instant error feedback.
Compiles to JavaScript — runs everywhere JS does, with full interop.
Based on ECMAScript standards, includes future JS features as proposals stabilize.