LearnThatStack Ace your next interview

TypeScript

TypeScript 5.x and 6.0, with strict mode on Checked Oct 2026 126 code snippets

The model

  • TypeScript is JavaScript plus static types. The compiler checks them before the code runs, then erases them, so the JavaScript that runs has none.
  • Types are structural. A value fits a type when it has the members the type asks for, whatever its class or name.
  • Types are gone at runtime, so as and interfaces can't check any data. A value from an API or JSON.parse needs a real check in code.
  • On a union such as string | number, you can use only what every member shares. A check like typeof narrows the type inside its branch.
  • Under strict mode, null and undefined are separate types, so the compiler makes you handle a missing value. strict is on by default since TypeScript 6.0.
.ts filescode and typesType checkertscJavaScripttypes erasedRuntimebrowser or NodeCHECKtsctype errorsBUILDemitVite, esbuild stripRUNrunnode app.ts strips
Only the type checker reads your types. Vite, esbuild and Node strip them without a check, so no type reaches the code that runs.

Types & inference

annotate what you must, let the compiler infer the rest

Objects & interfaces

describe what an object must have

Interface or type alias

Name a union or tupletype Id = A | BOnly type can
Extend a shapeinterface B extends Atype C = A & B
Reopen an interfaceDeclare it again. They merge

TipThe handbook's rule is to use interface until you need a feature only type has.

TrapWhen two parent types disagree on a property's type, extends reports an error. An intersection quietly makes it never.

Unions & narrowing

hold one of several types, then find out which

Classes & decorators

fields, access, inheritance and decorators

Extend, implement and abstract classes

Inherit from a base classclass B extends ACall super() before this
Implement interfacesclass A implements B, CMany allowed, but one extends
Make an abstract baseabstract area(): numberSubclasses must implement it

Trapimplements only checks the class. It doesn't type the method parameters, so they still need annotations.

Decorators

Use standard decoratorsTC39, the default since 5.0, no flag
Use the legacy decoratorsexperimentalDecoratorsNestJS uses them. They don't mix with TC39
Pass options to a decorator@Log("debug")A factory returns the decorator

TipA legacy property decorator runs once per class, not once per object, and never sees the initial value.

TrapA class decorator can add members at runtime, but the type system never sees them, so callers need as any.

Generics

write code once and keep the exact types

Covariance and contravariance

DogAnimalP<Dog>P<Animal>
covariantAn arrow means it can stand in for the target. A Producer, P, keeps the direction.
DogAnimalC<Dog>C<Animal>
contravariantA Consumer, C, flips it. C<Animal> can stand in for C<Dog>.
Use T as input and outputInvariant. Neither direction fits
Declare variance (4.7+)<out T><in T>

TrapMethod parameters stay bivariant, accepted in both directions, even under strictFunctionTypes.

Utility & mapped types

build new types from the ones you have

Mapped, conditional and template types

Map every property[K in keyof T]: string-? removes the ?
Rename or drop keys[K in keyof T as N]Map to never to drop one
Branch on a typeT extends U ? X : Yinfer E captures a part
Build string types`on${Capitalize<E>}`
Turn an array into a uniontypeof arr[number]After as const

TipRecursive types like DeepReadonly call themselves. Deep recursion slows the compiler and can hit its instantiation limit.

TrapOn a bare type parameter, a conditional type runs per union member. Wrap both sides, [T] extends [U], to stop it.

Compiler & tsconfig

compile, configure and resolve imports

Modules and namespaces

Export and importexport const x = 1import { x } from "./m"
Import a name only as a typeimport type { User }Removed from the output
Group code the old waynamespace Geo {}Use ES modules in new code

TipBundlers drop a module's unused exports. A namespace compiles to one object they can't trim.

TrapA file with no top-level import or export is a global script, not a module.

Module resolution and .d.ts files

Set moduleResolution"bundler""nodenext"For bundled apps, or Node.js
Add an import alias"paths": { "@/*": ["src/*"] }
Drop settings 6.0 deprecatednode mode and baseUrl fail in 7.0
Add a library's typesnpm i -D @types/lodashAnd @types/node for Node
Type an untyped packagedeclare module "lib"In a types-only .d.ts file

Trappaths only tells the compiler. The bundler or runtime needs the same alias, or the import fails when it runs.

Debug and speed up builds

Debug the .ts lines"sourceMap": true
Map Node stack traces to .tsnode --enable-source-maps app.js
Read a long intersectionFlatten it with Expand<T>
Skip checking .d.ts files"skipLibCheck": true
Check each project once"composite": truetsc --build
Get faster full buildsTypeScript 7.0, 8 to 12 times faster

TipSplit a long type into named steps and test each one with a literal. noErrorTruncation shows whole types.

React, async & API data

components, promises, errors and data you receive

React with TypeScript

Type a component's props({ label }: ButtonProps)
Type state that starts emptyuseState<User | null>(null)
Type a change eventChangeEvent<HTMLInputElement>And FormEvent for a submit
Type reducer actions{ type: "add" }A union tagged by type

Traptarget is where the event started, which can be a child. Use currentTarget for the element the handler is on.

Migration & large codebases

migrate, test, organise and stay type-safe at scale

Habits and anti-patterns

Stop copying typesDerive them with Pick, Omit, typeof and keyof
Silence one type error// @ts-expect-errorErrors once fixed, unlike @ts-ignore
Keep look-alike IDs apartstring & { __brand: "UserId" }A branded type

TipTurn on strict from the start, and prefer a union of strings to an enum.

TrapTwo types with the same shape are interchangeable. A UserId and an OrderId that are both string mix freely.

Design patterns

Allow only one instanceprivate constructor()Plus a static getInstance
Inject dependenciesInterface-typed constructor parameters
Type a builder chain exactlyEach call returns a wider builder type
Allow a call in one statethis: Req<"idle">A phantom type parameter
Chain steps that may be emptyAn Option type with map, flatMap

TrapA DI container can't find a dependency by its interface, because interfaces vanish at runtime. It maps tokens instead.

Found this useful? Pass it on.
Pro · $10/mo

This sheet stays free. With Pro, you can:

  • Every answer on this sheet. Read the full answer to all 102 questions it links, not just the 16 free ones.
  • My sheet. The cards you star and the ones you miss in Quiz me, from every sheet, on one page to quiz and print.
  • PDFs. Download this sheet and My sheet, laid out for A4, with every question a link to its answer.
  • The rest of Pro. Every answer in the question library, and 1,000 AI credits a month.