Skip to content
Home
TypeScript Utility Types: Partial, Pick, Omit, Record, and Custom...

TypeScript Utility Types: Partial, Pick, Omit, Record, and Custom...

TypeScript TypeScript 7 min read 1403 words Beginner ExcellentWiki Editorial Team

TypeScript ships with a comprehensive set of built-in utility types that transform existing types into new ones. These utilities reduce boilerplate, enforce consistency, and eliminate manual type duplication. Understanding them is essential for productive TypeScript development — they appear in virtually every production codebase. The TypeScript Handbook documents each utility type with examples (see TypeScript Utility Types). This guide covers every built-in utility type, explains its implementation using mapped/conditional types, and demonstrates advanced compositions.

Property Modification Utilities

Partial<T>

Partial<T> makes all properties in T optional. It is implemented as a mapped type:

type Partial<T> = {
  [P in keyof T]?: T[P];
---;

Use Partial for update operations where only a subset of fields is provided. Combined with the Pick utility, it enables precise API contract definitions:

interface User {
  id: number;
  name: string;
  email: string;
  role: 'admin' | 'user';
---

type UpdateUserPayload = Partial<Omit<User, 'id'>>;
// { name?: string; email?: string; role?: 'admin' | 'user' }

Required<T>

Required<T> is the inverse of Partial — it makes all properties required by removing the ? modifier:

type Required<T> = {
  [P in keyof T]-?: T[P];
---;

The -? syntax strips the optional modifier. Use Required when accepting a potentially partial input but needing a complete object internally:

interface Config {
  url?: string;
  timeout?: number;
  retries?: number;
---

function normalizeConfig(config: Config): Required<Config> {
  return {
    url: config.url ?? 'https://default.api',
    timeout: config.timeout ?? 5000,
    retries: config.retries ?? 3,
  };
---

The Required type ensures consumers of normalizeConfig always receive a fully populated configuration object without needing runtime checks.

Readonly<T>

Readonly<T> adds the readonly modifier to all properties, preventing reassignment after construction:

type Readonly<T> = {
  readonly [P in keyof T]: T[P];
---;

Use for immutable data models, configuration objects, and state snapshots.

Property Selection Utilities

Pick<T, K>

Pick<T, K> creates a type by selecting a subset of properties K from T:

type Pick<T, K extends keyof T> = {
  [P in K]: T[P];
---;
type UserPreview = Pick<User, 'name' | 'email'>;
// { name: string; email: string; }

Omit<T, K>

Omit<T, K> is the complement of Pick — it creates a type excluding the properties in K:

type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
type UserWithoutId = Omit<User, 'id'>;
// { name: string; email: string; role: 'admin' | 'user' }

Pick and Omit are the most frequently used utility types. They model projections of database entities, API response shaping, and component prop subsets.

Composition Utilities

Record<K, T>

Record<K, T> creates an object type with keys of type K and values of type T:

type Record<K extends keyof any, T> = {
  [P in K]: T;
---;
type PageRoutes = Record<'home' | 'about' | 'contact', string>;
// { home: string; about: string; contact: string; }

Record is ideal for lookup tables, enums with associated data, and configuration maps:

const errorMessages: Record<number, string> = {
  404: 'Not Found',
  500: 'Internal Server Error',
---;

Union Filtering Utilities

Exclude<T, U>

Exclude<T, U> removes types from a union that are assignable to U:

type Exclude<T, U> = T extends U ? never : T;

Because this is a conditional type on a bare type parameter, it distributes over T. If T is string | number | boolean and U is string, the result is number | boolean.

Extract<T, U>

Extract<T, U> is the complement — it keeps only the types in T that are assignable to U:

type Extract<T, U> = T extends U ? T : never;
type Numbers = Extract<string | number | boolean, number | string>;
// string | number

NonNullable<T>

NonNullable<T> removes null and undefined from a type:

type NonNullable<T> = T extends null | undefined ? never : T;
type T0 = NonNullable<string | number | undefined>; // string | number

Function Type Utilities

Parameters<T>

Parameters<T> extracts the parameter types of a function type as a tuple:

type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;
function greet(name: string, age: number): void {}
type GreetParams = Parameters<typeof greet>; // [string, number]

This utility is invaluable for wrapping functions while preserving their argument types. For example, creating a debounced version of any function:

function debounce<T extends (...args: any[]) => any>(
  fn: T,
  delay: number
): (...args: Parameters<T>) => void {
  let timer: ReturnType<typeof setTimeout>;
  return (...args: Parameters<T>) => {
    clearTimeout(timer);
    timer = setTimeout(() => fn(...args), delay);
  };
---

The Parameters<T> utility ensures the debounced function has the exact same type signature as the original, without manual overloads.

ReturnType<T>

ReturnType<T> extracts the return type of a function type:

type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : never;
type GreetReturn = ReturnType<typeof greet>; // void

ConstructorParameters<T> and InstanceType<T>

These mirror Parameters and ReturnType but for constructor functions:

class User { constructor(public id: number, public name: string) {} }
type UserParams = ConstructorParameters<typeof User>; // [number, string]
type UserInstance = InstanceType<typeof User>; // User

These utilities enable factory patterns and dependency injection systems to work with constructor types generically without losing type information.

Promise Utilities

Awaited<T>

Awaited<T> recursively unwraps Promise types. It is the type equivalent of await:

type Awaited<T> = T extends Promise<infer U> ? Awaited<U> : T;
type Result = Awaited<Promise<Promise<string>>>; // string

Useful for typing async function results without manual unwrapping:

type FetchResult = Awaited<ReturnType<typeof fetchData>>;

Template Literal and String Manipulation Utilities

TypeScript 4.1 introduced intrinsic string manipulation types: Uppercase<S>, Lowercase<S>, Capitalize<S>, and Uncapitalize<S>. These operate at the type level for converting string literal types:

type EventName = 'click' | 'focus';
type HandlerName = `on${Capitalize<EventName>}`; // 'onClick' | 'onFocus'

Event Handler Patterns

Combining template literal types with mapped types creates type-safe event systems:

type Events = 'userLogin' | 'userLogout' | 'error';
type EventHandlers = {
  [K in Events as `on${Capitalize<K>}`]: (payload: unknown) => void;
---;
// { onUserLogin: (payload: unknown) => void; onUserLogout: ... }

This pattern is used extensively in libraries like tRPC, Prisma, and EventEmitter3 to generate type-safe handler registrations from string union definitions.

Creating Custom Utility Types

Combining mapped types, conditional types, and key remapping (TypeScript 4.1+) enables custom utilities:

DeepPartial<T>

Makes all nested properties optional recursively:

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

PickByType<T, V>

Picks properties whose values match a given type:

type PickByType<T, V> = {
  [P in keyof T as T[P] extends V ? P : never]: T[P];
---;

type StringProps = PickByType<{ name: string; age: number; email: string }, string>;
// { name: string; email: string; }

NonFunctionKeys<T>

Extracts property keys that are not functions:

type NonFunctionKeys<T> = {
  [P in keyof T]: T[P] extends Function ? never : P;
---[keyof T];

Indexed access ([keyof T]) converts the mapped object into a union of its value types, filtering out keys where the value was never.

UnionToIntersection<T>

Converts a union to an intersection — useful for merging overloaded types:

type UnionToIntersection<U> =
  (U extends any ? (k: U) => void : never) extends
  (k: infer I) => void ? I : never;

type Result = UnionToIntersection<{ a: string } | { b: number }>;
// { a: string } & { b: number }

This uses the contravariant position trick: in a union ((k: A) => void) | ((k: B) => void), the parameter in a contravariant position becomes A & B.

FAQ

What is the difference between Omit and Exclude?
Omit works on object types, removing specified keys. Exclude works on union types, removing specified members. Omit<User, 'id'> removes the id property; Exclude<string | number, string> removes string from the union.

Can I use utility types with classes?
Yes. Utility types work with class types the same way they work with interfaces and object type aliases. Partial<MyClass> makes all class properties optional.

How do I make a type that picks properties by value type?
Use PickByType<T, V> (shown above) or KeysByType<T, V> to get just the keys, then Pick.

What does keyof any mean in Record’s definition?
keyof any resolves to string | number | symbol — the set of valid object keys. It constrains K to valid property keys.

Are utility types available at runtime?
No. Utility types are pure type-system constructs and are erased during compilation. They have zero runtime overhead.

Internal Links

References

Section: TypeScript 1403 words 7 min read Beginner 756 articles in section Report inaccuracy Back to top