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 David Chen

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 1251 articles in section Report inaccuracy Back to top
DC
David Chen Technology Editor

Technology Editor at ExcellentWiki covering programming, engineering, and consumer technology topics.

View author profile →