All 22 built-in utility types, each with the type it produces; every example compiles under --strict.
Object modifiers
interface User { id: number; name: string; email?: string }
type Draft = Partial<User>; // { id?: number; name?: string; email?: string }
type Full = Required<User>; // { id: number; name: string; email: string }
type Frozen = Readonly<User>; // { readonly id: number; readonly name: string; ... }Partial<T>makes every property optional: ideal for patches,update(id: number, patch: Partial<User>).Required<T>removes?, and theundefinedthat came with it.Readonly<T>blocks reassignment at compile time only.Object.freeze(obj)freezes at runtime and returnsReadonly<T>.- All three are shallow: nested objects keep their own modifiers.
Pick, Omit & Record
type Preview = Pick<User, 'id' | 'name'>; // { id: number; name: string }
type NewUser = Omit<User, 'id'>; // { name: string; email?: string }
type Role = 'admin' | 'editor';
const permissions: Record<Role, string[]> = { admin: ['*'], editor: ['posts'] };Pick<T, K>keeps the listed keys, andKmust be keys ofT.Omit<T, K>removes keys, butKis not checked:Omit<User, 'typo'>compiles silently.Record<K, V>builds an object type. With literal keys, every key is required (leaving outeditoris an error).Record<string, V>is the same type as{ [key: string]: V }.satisfies Record<string, string>checks the values while keeping the object’s exact keys.
Filtering unions
type Status = 'idle' | 'loading' | 'success' | 'error';
type Busy = Exclude<Status, 'idle'>; // 'loading' | 'success' | 'error'
type Done = Extract<Status, 'success' | 'error'>; // 'success' | 'error'
type Name = NonNullable<string | null | undefined>; // string
type Shape = { kind: 'circle'; r: number } | { kind: 'square'; side: number };
type Circle = Extract<Shape, { kind: 'circle' }>; // { kind: 'circle'; r: number }Exclude,ExtractandNonNullablefilter union members; to drop object keys, useOmit.Extractwith an object pattern pulls one variant out of a discriminated union.NonNullable<T>is defined asT & {}.
Functions & classes
function createUser(name: string, age?: number) {
return { id: 1, name, age };
}
type Args = Parameters<typeof createUser>; // [name: string, age?: number | undefined]
type Made = ReturnType<typeof createUser>; // { id: number; name: string; age: number | undefined }
type First = Parameters<typeof createUser>[0]; // string
class ApiClient { constructor(public baseUrl: string, public timeout = 5000) {} }
type CtorArgs = ConstructorParameters<typeof ApiClient>; // [baseUrl: string, timeout?: number]
type Client = InstanceType<typeof ApiClient>; // ApiClientParametersandReturnTypetake a function type: writetypeof fn, notfn.- On an overloaded function,
ReturnTypeuses the last overload. ConstructorParametersandInstanceTypetaketypeof Class, becauseApiClientused as a type already means the instance.
this helpers
function greet(this: { name: string }, greeting: string) {
return `${greeting}, ${this.name}`;
}
type Ctx = ThisParameterType<typeof greet>; // { name: string }
type Plain = OmitThisParameter<typeof greet>; // (greeting: string) => string
const hi: Plain = greet.bind({ name: 'Ada' });ThisParameterType<F>reads thethisparameter (unknownif there is none).OmitThisParameter<F>removes it, which is whatbindgives you.ThisType<T>is a marker: in an object literal typed asM & ThisType<X>,thisisX. It needsnoImplicitThis, whichstrictenables.
type Options<D, M> = { data: D; methods: M & ThisType<D & M> };
declare function define<D, M>(options: Options<D, M>): D & M;
const counter = define({
data: { count: 0 },
methods: { inc() { this.count++; } }, // this: { count: number } & { inc(): void }
});Awaited & NoInfer
type N = Awaited<Promise<Promise<number>>>; // number
async function loadUser() { return { id: 1 }; }
type Loaded = Awaited<ReturnType<typeof loadUser>>; // { id: number }
function pick<T extends string>(options: T[], fallback: NoInfer<T>): T {
return options[0] ?? fallback;
}
pick(['sm', 'md', 'lg'], 'md'); // ok
pick(['sm', 'md', 'lg'], 'xl'); // error: '"xl"' is not assignable to '"lg" | "md" | "sm"'Awaited<T>unwraps likeawait: recursively, including thenables. It is howPromise.alltypes its result.NoInfer<T>(TypeScript 5.4+) stops an argument from being used to inferT. Without it,Twidens to include'xl'and the typo compiles.
String manipulation
type Method = Uppercase<'get'>; // 'GET'
type Lower = Lowercase<'POST'>; // 'post'
type Key = Uncapitalize<'UserId'>; // 'userId'
type Handler = `on${Capitalize<'click' | 'focus'>}`; // 'onClick' | 'onFocus'
type Getters<T> = { [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K] };
type UserGetters = Getters<{ id: number; name: string }>; // { getId: () => number; getName: () => string }Uppercase,Lowercase,CapitalizeandUncapitalizeare compiler intrinsics for string literal types, applied to each member of a union.- Combine them with template literal types and key remapping (
as) to derive whole APIs.
How they’re built
type MyPartial<T> = { [K in keyof T]?: T[K] };
type MyPick<T, K extends keyof T> = { [P in K]: T[P] };
type MyOmit<T, K extends PropertyKey> = MyPick<T, Exclude<keyof T, K>>;
type MyExclude<T, U> = T extends U ? never : T;
type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : never;- Mapped types (
[K in keyof T]) powerPartial,Required,Readonly,PickandRecord. The-?and-readonlymodifiers remove flags. - Conditional types power
Exclude,Extractand theinfer-based helpers. A bare type parameter likeTinT extends Udistributes over unions.
Gotchas & DIY helpers
type DeepPartial<T> = { [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K] };
type Mutable<T> = { -readonly [K in keyof T]: T[K] };
type DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;Omitis not distributive.Omit<Shape, 'kind'>keeps only the keys shared by every member, then dropskind, leaving{}.DistributiveOmit<Shape, 'kind'>gives{ r: number } | { side: number }.- Shallow by design: use
DeepPartialfor nested config objects. - Compile-time only:
ReadonlyandMutablenever change runtime behavior. - Interview favorites: implement
Pick,Omit,ExcludeandReturnTypefrom memory, then explain whyOmitaccepts any key.