cheat sheet

Utility Types

TypeScript's built-in generic utility types that transform existing types into new ones. Covers Partial, Required, Readonly, Record, Pick, Omit, Exclude, Extract, ReturnType, Awaited, and more.

Utility Types

What it is

TypeScript ships a collection of built-in generic utility types that transform existing types into new ones. They are defined using mapped types, conditional types, and infer — all available in the TypeScript standard library without any import. Understanding them eliminates the need to repeat common type manipulations and helps you compose complex types from simpler ones.

Object utility types

Partial<T>

Makes all properties of T optional. Useful for update/patch payloads. Internal implementation:

typescript
type Partial<T> = { [K in keyof T]?: T[K] };
typescript
interface User {
  id: number;
  name: string;
  email: string;
  role: "admin" | "user";
}

type UserPatch = Partial<User>;
// { id?: number; name?: string; email?: string; role?: "admin" | "user" }

function updateUser(id: number, patch: Partial<User>): Promise<User> {
  return fetch(`/users/${id}`, {
    method: "PATCH",
    body: JSON.stringify(patch),
  }).then((r) => r.json());
}

updateUser(1, { name: "Alice" }); // Only name — rest are optional

Required<T>

Makes all properties required (removes all ?). The inverse of Partial. Internal implementation uses the -? modifier to strip optionality:

typescript
type Required<T> = { [K in keyof T]-?: T[K] };
typescript
interface Config {
  host?: string;
  port?: number;
  debug?: boolean;
}

type ResolvedConfig = Required<Config>;
// { host: string; port: number; debug: boolean }

function resolveConfig(cfg: Config): ResolvedConfig {
  return {
    host: cfg.host ?? "localhost",
    port: cfg.port ?? 3000,
    debug: cfg.debug ?? false,
  };
}

Readonly<T>

Makes all properties readonly — they cannot be reassigned after creation. Implementation:

typescript
type Readonly<T> = { readonly [K in keyof T]: T[K] };
typescript
interface Point { x: number; y: number }

const origin: Readonly<Point> = { x: 0, y: 0 };
origin.x = 1; // Error — Cannot assign to 'x' because it is a read-only property

Useful for function parameters you want to protect from mutation:

typescript
function translate(point: Readonly<Point>, dx: number, dy: number): Point {
  // point.x += dx; // Error — can't mutate
  return { x: point.x + dx, y: point.y + dy }; // must return new object
}

Record<K, V>

Constructs an object type whose keys are K and values are V. Record is the canonical "map" type — its implementation is a one-line mapped type:

typescript
type Record<K extends keyof any, V> = { [P in K]: V };

The keyof any constraint allows K to be string | number | symbol (the only valid object key types).

typescript
type Role = "admin" | "user" | "moderator";
type Permissions = Record<Role, string[]>;

const permissions: Permissions = {
  admin:     ["read", "write", "delete"],
  user:      ["read"],
  moderator: ["read", "write"],
};

// Also useful for dictionaries
type Cache = Record<string, unknown>;
const cache: Cache = {};
cache["key1"] = { data: 42 };

Pick<T, K>

Creates a type with only the specified keys from T. Implementation:

typescript
type Pick<T, K extends keyof T> = { [P in K]: T[P] };

The K extends keyof T constraint is what makes Pick safer than a hand-rolled equivalent — invalid keys are flagged at the call site.

typescript
type UserPreview = Pick<User, "id" | "name">;
// { id: number; name: string }

function renderCard(user: Pick<User, "id" | "name">): string {
  return `<div>${user.id}: ${user.name}</div>`;
}

Omit<T, K>

Creates a type with all keys from T except those in K. The inverse of Pick. Internal implementation is Pick over Exclude:

typescript
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

Note that K extends keyof any is intentionally loose — Omit silently accepts keys that don't exist on T, which is a known papercut. For strict checking, see Except from type-fest.

typescript
type UserWithoutId = Omit<User, "id">;
// { name: string; email: string; role: "admin" | "user" }

type CreateUserPayload = Omit<User, "id">; // id is generated server-side

Exclude<T, U>

From a union type T, removes all members assignable to U. Built from a distributive conditional type — see mapped-conditional-types for why distribution is what makes this work member-by-member:

typescript
type Exclude<T, U> = T extends U ? never : T;
typescript
type Status = "pending" | "active" | "inactive" | "deleted";

type ActiveStatus = Exclude<Status, "deleted" | "inactive">;
// "pending" | "active"

type NonNullish = Exclude<string | number | null | undefined, null | undefined>;
// string | number

Extract<T, U>

From a union type T, keeps only members assignable to U. The inverse of Exclude:

typescript
type Extract<T, U> = T extends U ? T : never;
typescript
type StringOrNumber = string | number | boolean | object;

type Primitives = Extract<StringOrNumber, string | number>;
// string | number

type SuccessResponse = Extract<
  { status: "ok"; data: string } | { status: "error"; message: string },
  { status: "ok" }
>;
// { status: "ok"; data: string }

NonNullable<T>

Removes null and undefined from a type. Internal implementation (TS 4.8+ uses an intersection trick for better inference):

typescript
type NonNullable<T> = T & {};
// Older form:
// type NonNullable<T> = T extends null | undefined ? never : T;
typescript
type MaybeString = string | null | undefined;
type DefiniteString = NonNullable<MaybeString>; // string

function assertNonNull<T>(value: T): NonNullable<T> {
  if (value == null) throw new Error("Value is null or undefined");
  return value as NonNullable<T>;
}

Function utility types

ReturnType<T>

Extracts the return type of a function type T. Implementation uses infer in the return position:

typescript
type ReturnType<T extends (...args: any) => any> =
  T extends (...args: any) => infer R ? R : any;
typescript
function createUser(name: string, role: string) {
  return { id: Math.random(), name, role, createdAt: new Date() };
}

type CreatedUser = ReturnType<typeof createUser>;
// { id: number; name: string; role: string; createdAt: Date }

// Useful when you don't want to define the return type separately

Parameters<T>

Extracts the parameter types of a function type T as a tuple. Implementation uses infer over the rest parameter:

typescript
type Parameters<T extends (...args: any) => any> =
  T extends (...args: infer P) => any ? P : never;
typescript
function login(username: string, password: string, rememberMe: boolean): void {}

type LoginArgs = Parameters<typeof login>;
// [username: string, password: string, rememberMe: boolean]

// Re-use parameters in a wrapper function
function withLogging(fn: (...args: Parameters<typeof login>) => void) {
  return (...args: Parameters<typeof login>) => {
    console.log("Calling login with", args[0]);
    fn(...args);
  };
}

ConstructorParameters<T>

Extracts the constructor parameter types of a class as a tuple, mirroring Parameters<T> but for new signatures. Useful in generic factory functions that need to forward constructor arguments without hardcoding them. Implementation:

typescript
type ConstructorParameters<T extends abstract new (...args: any) => any> =
  T extends abstract new (...args: infer P) => any ? P : never;
typescript
class HttpClient {
  constructor(
    public baseUrl: string,
    public timeout: number,
    public headers: Record<string, string>
  ) {}
}

type HttpClientArgs = ConstructorParameters<typeof HttpClient>;
// [baseUrl: string, timeout: number, headers: Record<string, string>]

InstanceType<T>

Extracts the instance type produced by a constructor type T — essentially the type of new T(). Pair it with ConstructorParameters<T> when writing generic factory helpers that need to return the correctly typed instance. Implementation:

typescript
type InstanceType<T extends abstract new (...args: any) => any> =
  T extends abstract new (...args: any) => infer R ? R : any;
typescript
type HttpClientInstance = InstanceType<typeof HttpClient>;
// HttpClient

// Useful in generic factory patterns
function createService<T extends abstract new (...args: any[]) => any>(
  cls: T,
  ...args: ConstructorParameters<T>
): InstanceType<T> {
  return new (cls as any)(...args);
}

ThisParameterType<T> and OmitThisParameter<T>

ThisParameterType<T> extracts the explicit this parameter type from a function signature; OmitThisParameter<T> strips it, giving you the callable signature without a this constraint. Use them when you need to bind or adapt functions that declare an explicit this type.

typescript
function greet(this: { name: string }, greeting: string): string {
  return `${greeting}, ${this.name}`;
}

type ThisType = ThisParameterType<typeof greet>; // { name: string }
type NoThis   = OmitThisParameter<typeof greet>; // (greeting: string) => string

ThisType<T>

ThisType<T> is a special marker utility — it has no runtime, and unlike the others it isn't a transformation. Instead, when it appears in a contextualType for an object literal, it tells the compiler to use T as the this type for every method in that literal. The classic use case is Vue 2-style options objects or any "configuration object whose methods see each other through this."

typescript
type ObjectDescriptor<D, M> = {
  data?: D;
  methods?: M & ThisType<D & M>; // <-- methods can `this.foo` over D and M
};

function makeObject<D, M>(desc: ObjectDescriptor<D, M>): D & M {
  return { ...(desc.data ?? {} as D), ...(desc.methods ?? {} as M) } as D & M;
}

const obj = makeObject({
  data: { x: 0, y: 0 },
  methods: {
    moveBy(dx: number, dy: number) {
      this.x += dx;
      this.y += dy;
      // this is inferred as { x: number; y: number } & methods — no annotation needed
    },
  },
});

obj.moveBy(5, 7);
console.log(obj);

Output:

less
{ x: 5, y: 7, moveBy: [Function: moveBy] }

Reach for ThisType only when you control the helper that consumes the object literal. Outside of that "options object" pattern it has no use.

Promise utility types

Awaited<T>

Recursively unwraps the type that a Promise resolves to. Handles nested promises. Internal implementation (added in TS 4.5) uses recursive conditional types with a then-method check, mirroring the spec's "thenable" semantics:

typescript
type Awaited<T> =
  T extends null | undefined ? T :
  T extends object & { then(onfulfilled: infer F, ...args: infer _): any } ?
    F extends (value: infer V, ...args: infer _) => any ? Awaited<V> : never :
  T;
typescript
type A = Awaited<Promise<string>>;                // string
type B = Awaited<Promise<Promise<number>>>;       // number
type C = Awaited<string>;                         // string (not a promise, passthrough)

async function fetchUser(): Promise<User> { /* ... */ return {} as User; }

type FetchedUser = Awaited<ReturnType<typeof fetchUser>>; // User

String manipulation types

These operate on string literal types. They are intrinsic — implemented in the compiler itself, not as user-land conditional types — so you cannot inspect their definition in lib.es5.d.ts. They are the four primitives every higher-level template-literal helper composes on top of.

typescript
type Upper = Uppercase<"hello">;          // "HELLO"
type Lower = Lowercase<"WORLD">;          // "world"
type Cap   = Capitalize<"typescript">;    // "Typescript"
type Uncap = Uncapitalize<"TypeScript">; // "typeScript"

// Practical: generate CSS class names from a union
type Colors = "red" | "green" | "blue";
type ColorClass = `text-${Colors}`;      // "text-red" | "text-green" | "text-blue"
type CssVar = `--color-${Lowercase<"RED" | "GREEN">}`; // "--color-red" | "--color-green"

A subtle note: when fed a non-literal string, these intrinsics return string — they only narrow when given a literal type. See template-literal-types for the full pattern library these enable. For richer case conversions (CamelCase, KebabCase, SnakeCase, PascalCase), use type-fest.

Composition patterns

Partial + Pick (partial update of specific fields)

Combining Partial and Pick lets you express "zero or more of these specific fields" — a common shape for PATCH-style API payloads where only a named subset of properties is editable.

typescript
type PartialPick<T, K extends keyof T> = Partial<Pick<T, K>>;

// Allow updating only name and email, both optional
type UserContactUpdate = PartialPick<User, "name" | "email">;
// { name?: string; email?: string }

Building Mutable<T> from scratch

The standard library doesn't include Mutable (the inverse of Readonly), but it's easy to build using a mapped type with the -readonly modifier:

typescript
type Mutable<T> = {
  -readonly [K in keyof T]: T[K];
};

interface FrozenPoint {
  readonly x: number;
  readonly y: number;
}

type MovablePoint = Mutable<FrozenPoint>;
// { x: number; y: number }  — readonly removed

const p: MovablePoint = { x: 0, y: 0 };
p.x = 10; // OK now

DeepPartial<T>

The built-in Partial is shallow. A recursive version:

typescript
type DeepPartial<T> = T extends object
  ? { [K in keyof T]?: DeepPartial<T[K]> }
  : T;

interface Config {
  server: { host: string; port: number };
  db:     { url: string; pool: number };
}

type PartialConfig = DeepPartial<Config>;
// { server?: { host?: string; port?: number }; db?: { url?: string; pool?: number } }

Combining utility types

Utility types compose with & (intersection) and with each other as generic arguments, letting you build precise shapes from a single source type. Prefer composition over duplicating interface definitions when derived types share an origin.

typescript
interface ApiResponse<T> {
  data: T;
  meta: { total: number; page: number };
  errors: string[];
}

// A minimal public response with no errors field
type PublicResponse<T> = Readonly<Omit<ApiResponse<T>, "errors">>;

// Make only the meta required, data optional
type PartialResponse<T> = Required<Pick<ApiResponse<T>, "meta">> & Partial<Pick<ApiResponse<T>, "data">>;

Beyond the standard library

The standard library is deliberately small. For a much wider set of community utilities — PartialDeep, RequireAtLeastOne, Merge, Tagged, JsonValue, Get<T, "a.b.c">, etc. — see type-fest. The same article includes a side-by-side comparison of when to reach for the built-ins versus the community pack.

Common pitfalls

  1. Partial is shallowPartial<{ a: { b: number } }> makes a optional but a.b stays required. Use PartialDeep from type-fest or a hand-rolled recursive version.
  2. Omit accepts unknown keys silently — typos in K produce a result identical to the input. Prefer Except from type-fest for strict checking.
  3. Pick<T, K> plus a union widens — passing K = "a" | "b" | "missing" errors immediately; that's the good case. The bad case is K extends string in your own utilities, which lets typos through.
  4. Required discards the ? markers but not the undefined in unionsRequired<{ a?: string | undefined }> is { a: string | undefined }, not { a: string }. Combine with NonNullable if needed.
  5. Record<string, V> is too loosekeyof Record<string, V> is string, dropping any literal narrowness. Use Record<"a" | "b", V> (or just an explicit object type) when you want enumerable keys.
  6. ReturnType over an overloaded function — picks the last overload only. For all overload returns, use an explicit union or type-fest's OverloadedReturnType.
  7. Parameters widens optional parametersParameters<(x?: number) => void> is [x?: number], not [number]. Destructure carefully.
  8. Awaited and thenable detection — any object with a .then method is treated as a thenable. Real-world types (e.g. Bluebird<T>) work; oddly-shaped objects with a stray then field can confuse it.
  9. NonNullable<T> and indexed-accessUser["email"] where email?: string is string | undefined. Wrap with NonNullable to strip both, or use Required<User>["email"].
  10. Composing too many at onceRequired<Partial<Pick<Omit<T, "id">, "name" | "email">>> is hard to read. Extract intermediate aliases for clarity.

Real-world recipes

Recipe 1 — REST PATCH payload with required server-side fields excluded

Omit + Partial is the canonical shape for "any subset of editable fields." The server controls id/createdAt, so the client cannot send them.

typescript
interface UserRow {
  id: number;
  createdAt: Date;
  name: string;
  email: string;
  bio?: string;
}

type UserPatch = Partial<Omit<UserRow, "id" | "createdAt">>;

async function patchUser(id: number, body: UserPatch): Promise<void> {
  await fetch(`/users/${id}`, { method: "PATCH", body: JSON.stringify(body) });
}

await patchUser(1, { bio: "engineer" });          // OK
// await patchUser(1, { id: 2 });                 // Error — id is excluded

Recipe 2 — Discriminated extraction with Extract

When you have a discriminated union and you want the specific variant for one kind, Extract does it in one line — no manual narrowing function required.

typescript
type ApiResult =
  | { status: "ok"; data: string }
  | { status: "loading" }
  | { status: "error"; code: number; message: string };

type ErrorOnly = Extract<ApiResult, { status: "error" }>;
// { status: "error"; code: number; message: string }

function handleError(r: ErrorOnly): string {
  return `[${r.code}] ${r.message}`;
}

console.log(handleError({ status: "error", code: 500, message: "boom" }));

Output:

csharp
[500] boom

Recipe 3 — Lookup map from a literal union with Record

A Record<UnionLiteral, V> keyed on as const data gives you both type-checked keys and exhaustiveness — adding a new union member fails to compile until the map is updated.

typescript
type Status = "draft" | "published" | "archived";

const COLOR: Record<Status, string> = {
  draft:     "#888",
  published: "#0a0",
  archived:  "#a00",
};

function pill(s: Status): string {
  return `<span style="color:${COLOR[s]}">${s}</span>`;
}

console.log(pill("published"));

Output:

css
<span style="color:#0a0">published</span>

Recipe 4 — Forwarding a function signature with Parameters/ReturnType

A "logging wrapper" preserves both arguments and return type without naming them — invaluable for middleware patterns.

typescript
function withLogging<F extends (...args: any[]) => any>(label: string, fn: F): F {
  return ((...args: Parameters<F>): ReturnType<F> => {
    console.log(`[${label}]`, args);
    return fn(...args);
  }) as F;
}

const add = (a: number, b: number) => a + b;
const loggedAdd = withLogging("add", add);
console.log(loggedAdd(2, 3));

Output:

css
[add] [ 2, 3 ]
5

Recipe 5 — Generic instantiator with ConstructorParameters/InstanceType

A factory accepts any class and the right arguments, returning a correctly-typed instance — zero any, zero overloads.

typescript
function instantiate<C extends abstract new (...args: any) => any>(
  cls: C,
  ...args: ConstructorParameters<C>
): InstanceType<C> {
  return new (cls as any)(...args);
}

class Logger { constructor(public name: string, public level: "info" | "warn") {} }
class Cache  { constructor(public ttlMs: number) {} }

const log = instantiate(Logger, "api", "warn"); // Logger
const cache = instantiate(Cache, 60_000);       // Cache
console.log(log.name, cache.ttlMs);

Output:

code
api 60000

Recipe 6 — Branded literal map with Record + Capitalize

Generate route handler names from an event union, ensuring every event has a handler and that handler names follow a strict pattern.

typescript
type Event = "click" | "focus" | "blur";
type HandlerName = `on${Capitalize<Event>}`;

type Handlers = Record<HandlerName, (e: globalThis.Event) => void>;

const handlers: Handlers = {
  onClick: () => console.log("click"),
  onFocus: () => console.log("focus"),
  onBlur:  () => console.log("blur"),
};

handlers.onClick(new Event("click"));

Output:

arduino
click

Recipe 7 — Pick + Partial + Required composition

Sometimes you want "these fields required, those optional, and nothing else allowed" — an exact PATCH shape.

typescript
interface Form {
  email: string;
  password: string;
  bio?: string;
  marketing?: boolean;
}

type SignupSubmit =
  Required<Pick<Form, "email" | "password">> &
  Partial<Pick<Form, "bio" | "marketing">>;

const f: SignupSubmit = { email: "a@b.c", password: "secret123" };
const g: SignupSubmit = { email: "a@b.c", password: "secret123", bio: "hi" };
console.log(f, g);

Output:

css
{ email: 'a@b.c', password: 'secret123' } { email: 'a@b.c', password: 'secret123', bio: 'hi' }

Recipe 8 — Awaited over a chain of fetchers

When function composition yields nested promises, Awaited<ReturnType<...>> extracts the final settled value.

typescript
async function login(): Promise<{ token: string }> { return { token: "abc" }; }
async function profile(_t: string): Promise<Promise<{ name: string }>> {
  return Promise.resolve({ name: "Alice" });
}

type LoginResult   = Awaited<ReturnType<typeof login>>;   // { token: string }
type ProfileResult = Awaited<ReturnType<typeof profile>>; // { name: string }

async function loginAndFetch(): Promise<ProfileResult> {
  const { token } = await login();
  return profile(token);
}

console.log(await loginAndFetch());

Output:

css
{ name: 'Alice' }

Quick reference table

UtilityInputOutput
Partial<T>{ a: string }{ a?: string }
Required<T>{ a?: string }{ a: string }
Readonly<T>{ a: string }{ readonly a: string }
Record<K, V>"x"|"y", number{ x: number; y: number }
Pick<T, K>User, "id"|"name"{ id: number; name: string }
Omit<T, K>User, "id"User without id
Exclude<T, U>string|null, nullstring
Extract<T, U>string|number, stringstring
NonNullable<T>string|null|undefinedstring
ReturnType<T>() => numbernumber
Parameters<T>(a: string) => void[a: string]
ConstructorParameters<T>typeof MyClassconstructor params tuple
InstanceType<T>typeof MyClassMyClass
Awaited<T>Promise<string>string
Uppercase<S>"hello""HELLO"