References

TypeScript Utility Types

Complete guide to 30+ built-in utility types for type transformations

Core Patterns

  • When to Read This
  • Partial
  • Required
  • Readonly

When to Read This

  • Transforming existing types (making optional, readonly, etc.)
  • Extracting or omitting type properties
  • Working with function/constructor signatures
  • Avoiding manual type definitions

Partial

Makes all properties optional.

interface User {
  id: number;
  name: string;
  email: string;
}

// ✅ All properties optional
type PartialUser = Partial<User>;
// { id?: number; name?: string; email?: string; }

function updateUser(id: number, updates: Partial<User>) {
  // Can pass any subset of properties
}

updateUser(1, { name: "John" }); // ✅
updateUser(1, { email: "john@example.com" }); // ✅

Required

Makes all properties required.

interface Config {
  host?: string;
  port?: number;
  timeout?: number;
}

// ✅ All properties required
type RequiredConfig = Required<Config>;
// { host: string; port: number; timeout: number; }

function validateConfig(config: Required<Config>) {
  // All fields must be present
}

Readonly

Makes all properties readonly.

interface Mutable {
  x: number;
  y: number;
}

type Immutable = Readonly<Mutable>;
// { readonly x: number; readonly y: number; }

const point: Immutable = { x: 1, y: 2 };
point.x = 3; // ❌ Error: Cannot assign to 'x' because it is readonly

Record<Keys, Type>

Constructs object type with keys and value type.

// ✅ Map string keys to numbers
type Scores = Record<string, number>;
const scores: Scores = { math: 95, english: 87 };

// ✅ Map specific keys to User
type UserRoles = Record<"admin" | "user" | "guest", User>;
const roles: UserRoles = {
  admin: { id: 1, name: "Admin", email: "admin@example.com" },
  user: { id: 2, name: "User", email: "user@example.com" },
  guest: { id: 3, name: "Guest", email: "guest@example.com" },
};

Pick<Type, Keys>

Creates type by picking specific properties.

interface User {
  id: number;
  name: string;
  email: string;
  password: string;
  createdAt: Date;
}

// ✅ Pick only public fields
type PublicUser = Pick<User, "id" | "name" | "email">;
// { id: number; name: string; email: string; }

function displayUser(user: PublicUser) {
  // Only has id, name, email (no password)
}

Omit<Type, Keys>

Creates type by omitting specific properties.

interface User {
  id: number;
  name: string;
  email: string;
  password: string;
}

// ✅ Omit sensitive fields
type UserWithoutPassword = Omit<User, "password">;
// { id: number; name: string; email: string; }

type UserForm = Omit<User, "id" | "createdAt">;
// For creating new users (no id yet)

Exclude<UnionType, ExcludedMembers>

Excludes types from union.

type AllowedTypes = string | number | boolean | null;

// ✅ Exclude null
type NonNullableTypes = Exclude<AllowedTypes, null>;
// string | number | boolean

type Status = "pending" | "approved" | "rejected" | "cancelled";

// ✅ Exclude terminal states
type ActiveStatus = Exclude<Status, "approved" | "rejected">;
// 'pending' | 'cancelled'

Extract<Type, Union>

Extracts types assignable to union.

type AllTypes = string | number | boolean | (() => void);

// ✅ Extract only functions
type FunctionTypes = Extract<AllTypes, Function>;
// () => void

type Status = "idle" | "loading" | "success" | "error";

// ✅ Extract error states
type ErrorStates = Extract<Status, "error">;
// 'error'

NonNullable

Excludes null and undefined.

type MaybeString = string | null | undefined;

// ✅ Remove null and undefined
type DefiniteString = NonNullable<MaybeString>;
// string

function process(value: NonNullable<MaybeString>) {
  console.log(value.toUpperCase()); // Safe: never null/undefined
}

ReturnType

Extracts return type of function.

function getUser() {
  return { id: 1, name: "John" };
}

// ✅ Extract return type
type User = ReturnType<typeof getUser>;
// { id: number; name: string; }

type AsyncReturnType<T extends (...args: any) => Promise<any>> = T extends (
  ...args: any
) => Promise<infer R>
  ? R
  : never;

async function fetchData() {
  return { data: [1, 2, 3] };
}

type Data = AsyncReturnType<typeof fetchData>;
// { data: number[]; }

Parameters

Extracts parameter types of function.

function createUser(name: string, age: number, email?: string) {
  // ...
}

// ✅ Extract parameter types as tuple
type CreateUserParams = Parameters<typeof createUser>;
// [name: string, age: number, email?: string | undefined]

function wrapper(...args: Parameters<typeof createUser>) {
  return createUser(...args); // Type-safe forwarding
}

ConstructorParameters

Extracts constructor parameter types.

class User {
  constructor(
    public name: string,
    public age: number,
  ) {}
}

// ✅ Extract constructor params
type UserParams = ConstructorParameters<typeof User>;
// [name: string, age: number]

function createUser(...args: ConstructorParameters<typeof User>) {
  return new User(...args);
}

InstanceType

Extracts instance type of constructor.

class User {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
}

// ✅ Extract instance type
type UserInstance = InstanceType<typeof User>;
// User

function processUser(user: InstanceType<typeof User>) {
  console.log(user.name);
}

ThisParameterType

Extracts this parameter type.

function toHex(this: Number) {
  return this.toString(16);
}

// ✅ Extract this type
type ThisType = ThisParameterType<typeof toHex>;
// Number

OmitThisParameter

Removes this parameter from function type.

function greet(this: User, message: string) {
  console.log(`${this.name}: ${message}`);
}

// ✅ Remove this parameter
type GreetFunction = OmitThisParameter<typeof greet>;
// (message: string) => void

Awaited

Recursively unwraps Promise types.

type NestedPromise = Promise<Promise<Promise<string>>>;

// ✅ Unwrap to string
type Unwrapped = Awaited<NestedPromise>;
// string

async function fetchData(): Promise<{ id: number; name: string }> {
  return { id: 1, name: "John" };
}

type Data = Awaited<ReturnType<typeof fetchData>>;
// { id: number; name: string; }

Uppercase

Converts string literal to uppercase.

type Greeting = "hello";

// ✅ Convert to uppercase
type LoudGreeting = Uppercase<Greeting>;
// 'HELLO'

type HttpMethod = "get" | "post" | "put" | "delete";
type UpperMethod = Uppercase<HttpMethod>;
// 'GET' | 'POST' | 'PUT' | 'DELETE'

Lowercase

Converts string literal to lowercase.

type LoudGreeting = "HELLO";

// ✅ Convert to lowercase
type Greeting = Lowercase<LoudGreeting>;
// 'hello'

Capitalize

Capitalizes first letter.

type Name = "john";

// ✅ Capitalize
type CapitalizedName = Capitalize<Name>;
// 'John'

Uncapitalize

Uncapitalizes first letter.

type Name = "John";

// ✅ Uncapitalize
type LowercaseName = Uncapitalize<Name>;
// 'john'

Advanced Combinations

✅ Partial + Pick

// Make specific properties optional
type PartialUpdate<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;

interface User {
  id: number;
  name: string;
  email: string;
}

// id is required, name and email are optional
type UserUpdate = PartialUpdate<User, "name" | "email">;
// { id: number; name?: string; email?: string; }

✅ Required + Pick

// Make specific properties required
type RequireFields<T, K extends keyof T> = T & Required<Pick<T, K>>;

interface Config {
  host?: string;
  port?: number;
  debug?: boolean;
}

// Require host and port
type ValidConfig = RequireFields<Config, "host" | "port">;
// { host: string; port: number; debug?: boolean; }

✅ Readonly + Partial

// Immutable partial object
type ImmutablePartial<T> = Readonly<Partial<T>>;

type PartialReadonlyUser = ImmutablePartial<User>;
// { readonly id?: number; readonly name?: string; readonly email?: string; }

Custom Utility Types

✅ DeepPartial

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

interface NestedConfig {
  server: {
    host: string;
    port: number;
  };
  database: {
    url: string;
    pool: { min: number; max: number };
  };
}

// ✅ All nested properties optional
type PartialConfig = DeepPartial<NestedConfig>;

✅ DeepReadonly

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

✅ Mutable (Opposite of Readonly)

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

interface ReadonlyUser {
  readonly id: number;
  readonly name: string;
}

type MutableUser = Mutable<ReadonlyUser>;
// { id: number; name: string; }

✅ NonEmptyArray

type NonEmptyArray<T> = [T, ...T[]];

function process(items: NonEmptyArray<number>) {
  console.log(items[0]); // Safe: always has at least one element
}

process([1, 2, 3]); // ✅
process([]); // ❌ Error

References