Cheatsheet
TypeScript types, narrowing, and generics
Every built in utility type with a one line example, the narrowing patterns the compiler actually understands, and the type level operators you need before you can read anyone else's generics. Grouped by what you are trying to express, not by name.
Utility types that reshape objects
| Type | Produces |
|---|---|
| Partial<User> | Every property optional. The classic patch or update payload. |
| Required<Config> | Every property required, stripping the optional markers. |
| Readonly<State> | Every property readonly, one level deep only. |
| Pick<User, 'id' | 'email'> | Keeps only the named keys. Errors if you name a key that does not exist. |
| Omit<User, 'password'> | Drops the named keys. Does not error on a key that does not exist. |
| Record<string, User> | Object with those key and value types. Record with a union key is exhaustive. |
| Exclude<'a' | 'b' | 'c', 'c'> | Removes union members assignable to the second type. Yields 'a' | 'b'. |
| Extract<Shape, { kind: 'circle' }> | Keeps only the union members assignable to the second type. |
| NonNullable<string | null> | Strips null and undefined from a union. |
| Awaited<Promise<User>> | Unwraps promises recursively, exactly like await does. |
| Parameters<typeof fn> | Tuple of a function's parameter types. |
| ReturnType<typeof fn> | A function's return type. Wrap in Awaited for an async function. |
| ConstructorParameters<typeof Cls> | Tuple of a constructor's parameter types. |
| InstanceType<typeof Cls> | The instance type a class constructor produces. |
| ThisParameterType<typeof fn> | The declared this parameter, or unknown when there is none. |
| OmitThisParameter<typeof fn> | The same function type with its this parameter removed. |
| NoInfer<T> | Blocks inference at that position, so another parameter decides T. TS 5.4. |
| Uppercase<'get'> | Intrinsic string type transform. Also Lowercase, Capitalize, Uncapitalize. |
Gotcha: Omit does not check the key you pass, so a rename silently stops omitting anything. Pick does check, so prefer it where you can, or write Omit<User, keyof User & 'password'> when you want the check.
Narrowing a union to one member
| Pattern | Narrows because |
|---|---|
| typeof x === 'string' | Typeof guard. Works for string, number, bigint, boolean, symbol, undefined, object, function. |
| x instanceof Error | Instanceof guard for anything with a prototype chain. |
| 'radius' in shape | The in operator narrows by property presence. |
| if (shape.kind === 'circle') | Discriminated union: a literal typed field narrows the whole object. |
| if (x === null) return | Early return narrows the rest of the function body. |
| Array.isArray(x) | A built in type predicate. |
| function isUser(x: unknown): x is User | Custom type predicate. You are asserting it; the compiler does not verify the body. |
| function assertOk(x: unknown): asserts x is Ok | Assertion function: narrows everything after the call site. |
| const isNum = (x: unknown) => typeof x === 'number' | Since TS 5.5 the predicate is inferred, so no explicit x is number needed. |
| x satisfies never | Exhaustiveness check in a default branch: compiles only if every case is handled. |
| x?.y?.z ?? fallback | Optional chaining plus nullish coalescing. Only null and undefined trigger the fallback. |
| x!.y | Non null assertion. Silences the compiler and changes nothing at runtime. |
type Shape =
| { kind: 'circle'; radius: number }
| { kind: 'rect'; w: number; h: number };
function area(s: Shape): number {
switch (s.kind) {
case 'circle': return Math.PI * s.radius ** 2;
case 'rect': return s.w * s.h;
default: {
const never: never = s; // add a case and this line errors
throw new Error(`unhandled ${never}`);
}
}
} Gotcha: narrowing is lost across a callback or an await if the value is a mutable let or an object property. Copy it into a const first and narrow that.
Generics and constraints
| Signature | Meaning |
|---|---|
| function id<T>(x: T): T | T is inferred from the argument at each call site. |
| <T extends object> | Constraint: T must be assignable to object. |
| <T, K extends keyof T>(obj: T, key: K) => T[K] | The canonical typed getter. K is a key of T and the return is that property's type. |
| <T = string> | Default type argument, used when inference finds nothing. |
| <const T extends readonly string[]> | Const type parameter: infers literal types without as const at the call site. TS 5.0. |
| <T extends unknown[]>(...args: T) | Variadic tuple capture, the basis of typed wrappers and decorators. |
| interface Box<T> { value: T } | Generic interface. Same syntax on type aliases and classes. |
| type Result<T, E = Error> = { ok: true; value: T } | { ok: false; error: E } | Generic discriminated union. The idiomatic alternative to throwing. |
Gotcha: a type parameter that appears only once in a signature is not doing anything a plain type could not. If T shows up in exactly one place, delete it and write the type directly.
Type level operators
| Operator | Meaning |
|---|---|
| keyof User | Union of the object's key names as string literals. |
| typeof config | Lifts a runtime value into its type. The bridge from value space to type space. |
| User['email'] | Indexed access: the type of one property. |
| User[keyof User] | Union of all property value types. |
| Item[number] | Element type of an array or tuple type. |
| T extends U ? X : Y | Conditional type. Distributes over a naked union type parameter. |
| [T] extends [U] ? X : Y | Tuple wrapping switches distribution off, which is usually what you meant. |
| T extends Array<infer E> ? E : never | Infer captures a type from a pattern match. |
| { [K in keyof T]: T[K] } | Mapped type. This exact form is the identity mapping. |
| { -readonly [K in keyof T]-?: T[K] } | Modifier removal: strips readonly and optional. Plus adds them. |
| { [K in keyof T as `get${Capitalize<K & string>}`]: () => T[K] } | Key remapping with a template literal type. Generates a getter interface. |
| `${Method} /${string}` | Template literal type. Constrains strings to a shape at compile time. |
| A & B | Intersection: must satisfy both. On primitives it usually yields never. |
| A | B | Union: one or the other. You may only touch members common to both until you narrow. |
Gotcha: a conditional type distributes when the checked type is a bare type parameter, so T extends string ? 'y' : 'n' with T = string | number gives 'y' | 'n', not 'n'. Wrap both sides in tuples to stop it.
Declaring types without fighting the compiler
| Syntax | Use when |
|---|---|
| as const | You want literal types and deep readonly instead of widened string and number. |
| const c = { a: 1 } satisfies Config | You want the value checked against a type but keep its narrow inferred type. TS 4.9. |
| const c: Config = { a: 1 } | You want the annotation to win and widen the value to that type. |
| x as unknown as T | A double assertion. Treat every one you write as a bug to revisit. |
| interface Props { ... } | Object shapes you may want to extend or that library consumers augment. |
| type Props = { ... } | Everything else: unions, tuples, mapped and conditional types. |
| const Role = { Admin: 'admin' } as const | The const object plus union pattern. Prefer it to enum, which emits runtime code. |
| type Role = typeof Role[keyof typeof Role] | Derive the value union from that const object. |
| import type { User } from './types' | Type only import that is guaranteed to be erased. Required by isolatedModules. |
| declare module 'untyped-lib' | Add types for a dependency that ships none. |
| using handle = open(path) | Explicit resource management: disposes at scope exit. TS 5.2. |
| function f(this: Window, e: Event) | Type the this binding as a fake first parameter. Erased at compile time. |
Gotcha: as is not a cast, it is a promise to the compiler. It performs no runtime check, so an as User on parsed JSON is exactly as safe as any. Validate at the boundary instead.
tsconfig flags that change your day
| Flag | Effect |
|---|---|
| "strict": true | Turns on the whole strict family. Non negotiable on a new project. |
| "noUncheckedIndexedAccess": true | arr[0] becomes T | undefined. Noisy, correct, and not part of strict. |
| "exactOptionalPropertyTypes": true | Distinguishes a missing property from one explicitly set to undefined. |
| "noImplicitOverride": true | Requires the override keyword, catching renamed base class methods. |
| "verbatimModuleSyntax": true | Import and export statements are emitted exactly as written. Pairs with import type. |
| "moduleResolution": "bundler" | Resolution that matches Vite, esbuild, and friends. TS 5.0. |
| "noEmit": true | Type check only, because the bundler is doing the transpiling. |
| "skipLibCheck": true | Skip type checking of declaration files. Big speedup, small risk. |
| "target": "ES2022" | Sets both the emitted syntax and the default lib. Raise it before you shim. |
| tsc --noEmit --watch | A dedicated type check loop next to your dev server. |
Gotcha: a bundler that strips types does not type check. Vite, esbuild, and SWC will happily ship code that tsc rejects, so a separate tsc --noEmit step in CI is mandatory, not optional.
Keep going
Most of these types end up in component props, so the React hooks cheatsheet is the natural next page. On the data side, Prisma vs Drizzle covers which ORM gives you better inferred types.
The frontend tool directory lists the frameworks these types travel through, and the cheatsheet index has the rest of the quick references.