Cheatsheet

Cheatsheet

Decision: interface vs type

NeedUse
Object shape, might be extended/merged laterinterface
Union, tuple, primitive alias, mapped/conditional typetype
Intersection compositiontype (A & B)
Rule of thumbdefault interface; reach for type only when interface can't express it

Decision: which utility type

NeedUtility
Make every field optionalPartial<T>
Make every field requiredRequired<T>
Make every field readonlyReadonly<T>
Build a dictionary with a fixed key setRecord<K, T>
Keep only some fieldsPick<T, K>
Drop some fieldsOmit<T, K>
Remove union members matching XExclude<T, X>
Keep only union members matching XExtract<T, X>
Strip null/undefinedNonNullable<T>
Get a function's param typesParameters<T>
Get a function's return typeReturnType<T>
Get a class's instance typeInstanceType<typeof C>
Unwrap nested PromisesAwaited<T>

Decision: narrowing technique

Value shapeTechnique
Union of primitivestypeof x === "..."
Union of class instancesx instanceof Class
Union of object shapes, one has a distinguishing prop"prop" in x
Union of object shapes with a shared literal discriminantcheck the discriminant (x.kind === "...")
Custom runtime checkuser-defined type guard: function isX(v): v is X
Filtering null/undefined from primitivestruthiness check (careful: also filters 0/"") or != null

Decision: module compiler options by consumer

ConsumermodulemoduleResolution
Bundler (webpack/esbuild/Vite), tsxesnextbundler
Node.js directly, ts-nodenodenext(implied) nodenext
Published library via tscnode18(implied) node16)
Bundled libraryesnextbundler (+ bundle declarations too)

readonly / immutability quick reference

GoalMechanism
Block reassigning a variableconst
Block reassigning one propertyreadonly prop: T
Block reassigning every property of a typeReadonly<T>
Freeze literal types on a literalas const
Block mutating array contentsreadonly T[] / ReadonlyArray<T>
None of the above are runtime-enforceduse Object.freeze / JS #private for real enforcement

any vs unknown vs never

TypeAcceptsGives back
anyeverythingeverything (no checking)
unknowneverythingnothing until narrowed
nevernothing (but itself)everything (vacuously)

Function compatibility quick rules

  • Fewer parameters in source = OK to assign (extra target params ignored — matches JS callback conventions).
  • More required parameters in source than target = error.
  • Return type: source must be a subtype of target's return type (not relaxed).
  • Parameter types: bivariant by default (either direction OK) unless strictFunctionTypes is on.

enum gotchas

  • keyof EnumName ≠ what you want — use keyof typeof EnumName for key names.
  • String enums get no reverse mapping; numeric enums do.
  • const enum is fully inlined — never publish one in a .d.ts for external consumers (cross-version value-mismatch risk).

Class visibility at a glance

ModifierCompile-time only?Visible from
public (default)anywhere
protectedyesclass + subclasses (not sibling subclasses)
privateyes (soft)declaring class only, cross-instance OK
#field (JS private)no (runtime-enforced)declaring class only

Generics red flags (reconsider if you see these)

  • Type parameter used only once across the whole signature → drop the generic, use a concrete type.
  • Constraining instead of using the parameter directly (<T extends any[]>(x: T) vs <T>(x: T[])) → degrades inferred return type.
  • A generic class's static member referencing the class's own type parameter → not allowed, only one shared static slot exists at runtime.