rgoussu@goussu: ~/library/frontend
~/library/frontend cat typescript-deep-dive.md

TypeScript deep dive

# The type system as a design tool — structural typing, narrowing, generics, the strictness dials, and types that make invalid states unrepresentable.

Conceptsaved 2026-08-08updated 2026-08-10 #typescript#javascript#types#frontend

Overview

TypeScript is a structural type system bolted onto JavaScript with zero runtime presence — types are erased at compile time, which defines both its power (gradual adoption, models any JS pattern) and its limits (no runtime guarantees; external data needs validation). Going deep means moving from "annotating variables" to using the type system as a design tool: encoding domain rules so that invalid states fail to compile.

Key points

  • Structural, not nominal: compatibility is by shape — any object with the right members satisfies an interface. Powerful and occasionally surprising (two unrelated types unify); branded types (string & { __brand: 'UserId' }) opt back into nominal-style distinctions where identity matters.
  • Unions + narrowing are the heart: model alternatives as unions, and let control flow narrow them — typeof/in guards, discriminated unions (a literal kind field) with exhaustive switch + never checks. This is the "make invalid states unrepresentable" pattern: {status:'loading'} | {status:'ok', data} | {status:'error', error} instead of nullable fields that lie.
  • The strictness dials decide what the types are worth: strict: true as the floor, noUncheckedIndexedAccess for honest arrays/records; any silently disables checking transitively — unknown is the honest "don't know yet" that forces narrowing.
  • The type-level toolkit: generics with constraints, keyof/indexed access, mapped and conditional types, infer, template-literal types — the mechanics behind utility types (Pick, Omit, ReturnType). Use to derive types from single sources of truth (as const + typeof) rather than duplicating shapes; resist type-level cleverness that reads like a puzzle.
  • Types end at runtime boundaries: API responses, env vars, and file contents are unknown wearing a costume — validate at the edge (Zod-style schema → inferred static type: one definition, both worlds) instead of as-casting hope into the codebase.
  • Ecosystem mechanics: tsc type-checks while faster transpilers do the emitting; declaration files (.d.ts) carry types for JS libraries; ESM/CJS module settings remain the sharpest operational edge.
  • To explore: variance (why function parameters are special + method bivariance), satisfies vs. annotation vs. assertion, monorepo project references, the type-system's Turing-completeness (as a warning, mostly).

Practice

  • Exercism TypeScript track (source) — small mentored exercises to make narrowing, unions, and generics reflexive.
  • type-challenges (source) — the type-level toolkit as a kata ladder: mapped/conditional types, infer, template literals — implement Pick before you reach for it.
  • Validate a real edge with Zod (source) — take an API your code consumes, write the schema, infer the static type from it, and delete every as cast on that path; one definition, both worlds, in practice.
  • Coding Challenges in TypeScript (source) — build a JSON parser or wc clone with a discriminated-union AST and exhaustive switches; "invalid states unrepresentable" on a real program.

Related