TypeScript
TypeScript adds static types, interfaces and generics to JavaScript and compiles to plain JS. Learn structural typing, the strict compiler, and how types turn runtime crashes into compile-time errors.
What is this?
TypeScript is a typed superset of JavaScript that compiles to plain, standards-compliant JS. It uses structural typing — compatibility is based on shape, not on names — and a strict compiler flags mismatches before code ever runs. Types are erased at compile time, so they add zero runtime cost.
Why do you need it?
- The compiler catches null dereferences, misspelled properties and shape mismatches at build time instead of in production.
- Editors surface autocomplete, rename refactors and inline type errors from the same type information.
- Types double as living documentation for the contracts between teams and services.
- Large refactors become mechanical and safe: the compiler lists every call site that must change.
- The community ships types for virtually every npm package, so typed code integrates cleanly.
Mental model
Architectural blueprints
TypeScript types
Blueprints specify rooms, doors and wiring before construction so mistakes are caught on paper. Types specify the shape of data and functions so mistakes are caught at compile time, not by users.
How it works
- tsc parses TypeScript source into an abstract syntax tree annotated with inferred types.
- The type checker walks the tree, resolving each expression's type and reporting errors without emitting anything.
- Structural typing compares object shapes: a type is assignable if it has every required member with a compatible type.
- Generics abstract over types so one function can serve many shapes while preserving exact relationships.
- The emitter strips type annotations and down-levels modern syntax to the configured ECMAScript target.
The TypeScript compile pipeline.
Key commands
npm install --save-dev typescriptAdd the TypeScript compiler to the project.
npx tsc --initGenerate a tsconfig.json with sensible defaults.
Created a new tsconfig.json with: ...
npx tsc --noEmitType-check the project without writing any output files.
npx tscType-check and emit JavaScript to the configured outDir.
npx tsc --watchRe-run the type checker whenever a file changes.
npx tsc --strict --noEmit src/index.tsType-check one file under the strictest settings.
Examples
A typed generic API client
interface User { id: number; name: string; active: boolean;} async function fetchJson<T>(url: string): Promise<T> { const response = await fetch(url); if (!response.ok) { throw new Error("Request failed: " + response.status); } return (await response.json()) as T;} function describeUsers(users: User[]): string { const active = users.filter((u) => u.active).map((u) => u.name); return active.length ? active.join(", ") : "No active users";} fetchJson<User[]>("https://api.example.com/users") .then((users) => console.log(describeUsers(users))) .catch((error) => console.error(error.message));The User interface defines the shape the code relies on. fetchJson<T> keeps the return type generic so callers get precise types. describeUsers is statically checked: passing User[] is enforced, and any mismatch fails at compile time.
In production
- Large codebases adopt TypeScript to keep refactoring safe; millions of repositories use it in production.
- API clients are generated from OpenAPI specs as typed clients, eliminating guesswork at call sites.
- Frontend state and domain models are typed once and shared between server and client.
- Config files and CI scripts use TypeScript (with ts-node or Bun) for self-documenting automation.
Best practices
- Enable strict mode and never turn it off to silence errors.
- Prefer unions, interfaces and generics over any; treat any as a defect.
- Use unknown instead of any for values whose type you must narrow at runtime.
- Keep types near the data they describe and export them from domain modules.
- Model impossible states with discriminated unions so exhaustive checks catch missing branches.
Common mistakes
- Skipping strict null checks, then crashing on null at runtime.
- Spreading any through the codebase until the compiler cannot help anymore.
- Writing verbose, redundant type annotations everywhere instead of letting inference work.
- Ignoring ts-ignore comments that hide real errors and rot over time.
Common issues
Property 'x' does not exist on type '{}'An object literal or API response was not given a type, so it inferred to an empty shape.
Define an interface and annotate the variable or function return type.
Cannot find module './types' or its corresponding type declarationsThe path is wrong, or the module is JS without a declaration file.
Fix the import path, or add a declaration file (module.d.ts) describing the module.
Type 'string | undefined' is not assignable to type 'string'Strict null checks flag a possibly-undefined value.
Guard with an if check, use a fallback with ??, or restructure the type to remove undefined.
Debugging tips
- Run npx tsc --noEmit after every change so the compiler is the first test.
- Hover any symbol in your editor to inspect its inferred type, then compare with what you intended.
- Use unknown plus type guards (typeof, Array.isArray, custom is) to narrow safely.
- Temporarily add an extra variable with an explicit annotation to expose where a mismatch begins.
Interview checkpoint
Hands-on lab
- Configure a strict tsconfig
- Define interfaces for API payloads
- Write a generic request helper
- Verify with tsc --noEmit
Type a Data Fetching Utility
30 min · intermediate
Quick quiz
Which tsconfig option enables the strictest null and assignment checks?
Type annotations add runtime overhead in the compiled JavaScript.
Which utility type makes every property of T optional?
Cheat sheet
string | numberUnion of allowed typesinterface User { name: string }Object shape definitionfunction f<T>(x: T): TGeneric functionconst v: unknown = ...Unknown that must be narrowedreadonlyImmutable propertyPartial<T>All properties optionalPick<T, 'id' | 'name'>Subset of propertiesRecord<K, V>Map of keys to valuesReturnType<typeof fn>Infer a function return type