Asher Cohen
Back to posts

TypeScript Utility Types

Essential built-in utility types that make working with TypeScript more efficient

Introduction

TypeScript provides a rich set of built-in utility types that help you transform and manipulate existing types. These utilities are part of the standard library and require no additional imports.

Core Utility Types

Partial

Makes all properties of a type optional.

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

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

// Perfect for update functions
function updateUser(id: number, updates: Partial<User>) {
  // ...
}

Required

Makes all properties of a type required.

interface Settings {
  theme?: 'light' | 'dark';
  notifications?: boolean;
}

type RequiredSettings = Required<Settings>;
// { theme: 'light' | 'dark'; notifications: boolean; }

Readonly

Makes all properties of a type readonly.

interface Config {
  apiUrl: string;
  timeout: number;
}

type ReadonlyConfig = Readonly<Config>;
// Cannot be modified after creation

Record<K, T>

Creates a type with a set of properties K of type T.

type RolePermissions = Record<'admin' | 'user' | 'guest', string[]>;
// {
//   admin: string[];
//   user: string[];
//   guest: string[];
// }

Pick<T, K>

Picks a subset of properties K from type T.

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

type PublicUser = Pick<User, 'id' | 'name'>;
// { id: number; name: string; }

Omit<T, K>

Omits a subset of properties K from type T.

type UserWithoutPassword = Omit<User, 'password'>;
// { id: number; name: string; email: string; }

Exclude<T, U>

Excludes types U from union type T.

type Status = 'pending' | 'loading' | 'success' | 'error';
type SuccessStatus = Exclude<Status, 'error'>;
// 'pending' | 'loading' | 'success'

Extract<T, U>

Extracts types U from union type T.

type Status = 'pending' | 'loading' | 'success' | 'error';
type ErrorStatus = Extract<Status, 'error'>;
// 'error'

NonNullable

Excludes null and undefined from type T.

type MaybeString = string | null | undefined;
type DefinitelyString = NonNullable<MaybeString>;
// string

ReturnType

Gets the return type of a function type.

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

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

Parameters

Gets the parameter types of a function.

function greet(name: string, age: number) {
  // ...
}

type GreetParams = Parameters<typeof greet>;
// [name: string, age: number]

InstanceType

Gets the instance type of a constructor function.

class User {
  constructor(public name: string) {}
}

type UserType = InstanceType<typeof User>;
// User

Practical Examples

API Response Handling

interface ApiResponse<T> {
  data: T;
  status: number;
  message: string;
}

type UserData = { id: number; name: string };
type UserResponse = ApiResponse<UserData>;

// Extract just the data type
type User = ApiResponse<UserData>['data'];

Form State Management

interface FormData {
  email: string;
  password: string;
  confirmPassword: string;
}

// Initial form state - all fields optional
type FormState = Partial<FormData>;

// Submitted form - errors for each field
type FormErrors = Record<keyof FormData, string | null>;

Configuration Objects

interface AppConfig {
  apiUrl: string;
  timeout: number;
  retries: number;
  debug: boolean;
}

// Environment-specific overrides
type EnvConfig = Partial<AppConfig>;

// Required production config
type ProductionConfig = Required<Pick<AppConfig, 'apiUrl' | 'timeout'>>;

Advanced Patterns

Combining Utilities

type ReadonlyPartial<T> = Readonly<Partial<T>>;

type Config = ReadonlyPartial<AppConfig>;

Conditional Utility

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

type WritableUser = Mutable<ReadonlyUser>;

When to Use Utility Types

Good use cases:

  • API response transformations
  • Form state management
  • Test data generation
  • Creating variations of existing types

Avoid when:

  • The type becomes unreadable
  • You're creating overly complex abstractions
  • Simple interfaces would work better

Conclusion

Utility types are powerful tools in your TypeScript toolkit. Master them to write more flexible, maintainable type definitions.

#typescript #types #programming