TypeScript Utility Types: Complete Guide

Every built-in utility type explained with practical examples. From Partial and Pick to custom mapped types, conditional types, and template literal types — the patterns you actually use in production TypeScript.

What Are Utility Types

TypeScript ships with a set of generic types that transform other types. These are called utility types. Instead of duplicating type definitions with small variations, you derive new types from existing ones.

Consider a User type. At different points in your application, you might need:

Without utility types, you would write four separate interfaces. With utility types, you write one and derive the rest.

typescript
interface User {
  id: string;
  name: string;
  email: string;
  password: string;
  avatar?: string;
  bio?: string;
}

// All fields optional (for PATCH /users/:id)
type UserUpdate = Partial<User>;

// Only name and email (for contact cards)
type UserContact = Pick<User, 'name' | 'email'>;

// Everything except password (for API responses)
type UserPublic = Omit<User, 'password'>;

// All fields required (for database insert)
type UserInsert = Required<User>;

All utility types are built into TypeScript. No imports needed. They work with any TypeScript version from 2.1+ (and later versions added more). You can test all the examples in this article using QTool's TypeScript Playground.

Partial and Required

Partial<T>

Partial<T> makes every property in T optional. This is the most commonly used utility type, and the one you will reach for whenever you write an update function.

typescript
interface Config {
  host: string;
  port: number;
  debug: boolean;
  logLevel: 'info' | 'warn' | 'error';
}

// All properties become optional
type PartialConfig = Partial<Config>;
// Equivalent to:
// {
//   host?: string;
//   port?: number;
//   debug?: boolean;
//   logLevel?: 'info' | 'warn' | 'error';
// }

function updateConfig(current: Config, updates: Partial<Config>): Config {
  return { ...current, ...updates };
}

// Valid: only updating what changed
updateConfig(defaultConfig, { port: 8080 });
updateConfig(defaultConfig, { debug: true, logLevel: 'error' });

Required<T>

Required<T> makes every property in T required (removes ? modifiers). It is the inverse of Partial.

typescript
interface FormData {
  name?: string;
  email?: string;
  age?: number;
}

// All properties become required
type CompleteFormData = Required<FormData>;

function submitForm(data: Required<FormData>): void {
  // TypeScript guarantees all fields are present
  console.log(data.name);   // string (not string | undefined)
  console.log(data.email);  // string
  console.log(data.age);    // number
}
Under the Hood

Partial is implemented as type Partial<T> = { [P in keyof T]?: T[P] }. It iterates over every key in T and adds the ? modifier. Required does the opposite: { [P in keyof T]-?: T[P] } — the -? removes optionality.

Pick and Omit

Pick<T, K>

Pick<T, K> creates a new type by selecting specific properties from T. Use it to create focused subtypes.

typescript
interface Article {
  id: string;
  title: string;
  body: string;
  author: string;
  createdAt: Date;
  updatedAt: Date;
  tags: string[];
  published: boolean;
}

// For article list: only the fields needed for a preview card
type ArticlePreview = Pick<Article, 'id' | 'title' | 'author' | 'createdAt'>;

// For article editor: only the editable fields
type ArticleEditable = Pick<Article, 'title' | 'body' | 'tags'>;

function renderPreviewCard(article: ArticlePreview): string {
  return `${article.title} by ${article.author}`;
  // article.body  <-- Error: Property 'body' does not exist
}

Omit<T, K>

Omit<T, K> creates a new type by removing specific properties. It is the inverse of Pick.

typescript
// Remove sensitive fields for API response
type ArticleResponse = Omit<Article, 'body'>;

// Remove auto-generated fields for creation
type ArticleCreate = Omit<Article, 'id' | 'createdAt' | 'updatedAt'>;

// API endpoint handler
async function createArticle(data: ArticleCreate): Promise<Article> {
  return {
    ...data,
    id: generateId(),
    createdAt: new Date(),
    updatedAt: new Date(),
  };
}

Pick vs Omit: When to Use Each

Scenario Use Why
Need 2-3 fields from a 10-field type Pick Shorter to list what you want
Need 8 fields from a 10-field type Omit Shorter to list what you don't want
Removing sensitive fields Omit Explicit about what is excluded
Creating a focused DTO Pick Explicit about what is included

If you are working with JSON API responses, the JSON to TypeScript converter can generate your base types automatically, then you can derive subtypes with Pick and Omit.

Record

Record<K, V> creates an object type where all keys are of type K and all values are of type V. It is the utility type for dictionaries and lookup tables.

typescript
// Simple dictionary
type UserMap = Record<string, User>;

// Enum-keyed lookup
type StatusMessages = Record<'success' | 'error' | 'pending', string>;

const messages: StatusMessages = {
  success: 'Operation completed.',
  error: 'Something went wrong.',
  pending: 'Processing your request...',
};

// Grouped data
type ArticlesByCategory = Record<string, Article[]>;

const articles: ArticlesByCategory = {
  javascript: [/* ... */],
  typescript: [/* ... */],
  css: [/* ... */],
};

Record with Computed Keys

typescript
// HTTP status code handler
type HttpStatus = 200 | 201 | 400 | 401 | 403 | 404 | 500;

type StatusHandlers = Record<HttpStatus, (response: Response) => void>;

const handlers: StatusHandlers = {
  200: (res) => console.log('OK'),
  201: (res) => console.log('Created'),
  400: (res) => console.error('Bad Request'),
  401: (res) => console.error('Unauthorized'),
  403: (res) => console.error('Forbidden'),
  404: (res) => console.error('Not Found'),
  500: (res) => console.error('Server Error'),
};

// TypeScript ensures every status is handled.
// Removing any entry causes a compile-time error.

Exclude and Extract

These two utility types work on union types, not object types. They filter members of a union.

Exclude<T, U>

Exclude<T, U> removes from T all members that are assignable to U.

typescript
type AllEvents = 'click' | 'scroll' | 'mousemove' | 'keydown' | 'keyup' | 'resize';

// Remove mouse events
type KeyboardEvents = Exclude<AllEvents, 'click' | 'scroll' | 'mousemove' | 'resize'>;
// Result: 'keydown' | 'keyup'

// Remove null and undefined from a union
type MaybeString = string | null | undefined;
type DefinitelyString = Exclude<MaybeString, null | undefined>;
// Result: string

Extract<T, U>

Extract<T, U> keeps only the members of T that are assignable to U. It is the inverse of Exclude.

typescript
type AllEvents = 'click' | 'scroll' | 'mousemove' | 'keydown' | 'keyup' | 'resize';

// Keep only mouse-related events
type MouseEvents = Extract<AllEvents, 'click' | 'mousemove' | 'scroll'>;
// Result: 'click' | 'mousemove' | 'scroll'

// Extract function types from a union
type Mixed = string | number | (() => void) | ((x: number) => string);
type Functions = Extract<Mixed, Function>;
// Result: (() => void) | ((x: number) => string)

ReturnType, Parameters, and Awaited

ReturnType<T>

ReturnType<T> extracts the return type of a function type. Use it when you need to type a variable to match what a function returns, without hardcoding the type.

typescript
function createUser(name: string, email: string) {
  return {
    id: crypto.randomUUID(),
    name,
    email,
    createdAt: new Date(),
  };
}

// Extract the return type without writing it manually
type CreatedUser = ReturnType<typeof createUser>;
// Result: { id: string; name: string; email: string; createdAt: Date }

// Useful for typing state that holds function results
let lastCreated: CreatedUser | null = null;

Parameters<T>

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

typescript
function sendEmail(to: string, subject: string, body: string, cc?: string[]): void {
  // ...
}

type EmailParams = Parameters<typeof sendEmail>;
// Result: [to: string, subject: string, body: string, cc?: string[]]

// Use it to create wrapper functions with matching signatures
function logAndSend(...args: Parameters<typeof sendEmail>): void {
  console.log(`Sending email to ${args[0]}: ${args[1]}`);
  sendEmail(...args);
}

Awaited<T>

Awaited<T> unwraps a Promise type to get the resolved value type. Added in TypeScript 4.5.

typescript
async function fetchUser(id: string): Promise<User> {
  const response = await fetch(`/api/users/${id}`);
  return response.json();
}

type FetchedUser = Awaited<ReturnType<typeof fetchUser>>;
// Result: User  (unwraps Promise<User> to User)

// Works with nested Promises too
type DeepPromise = Promise<Promise<Promise<string>>>;
type Resolved = Awaited<DeepPromise>;
// Result: string

NonNullable and Readonly

NonNullable<T>

NonNullable<T> removes null and undefined from a type. It is a shorthand for Exclude<T, null | undefined>.

typescript
type MaybeUser = User | null | undefined;

type DefiniteUser = NonNullable<MaybeUser>;
// Result: User

// Common pattern: after null-check guards
function processUser(user: MaybeUser): void {
  if (!user) return;
  // TypeScript narrows to User here, but sometimes
  // you need the type explicitly for generics
  const validUser: NonNullable<MaybeUser> = user;
}

Readonly<T>

Readonly<T> makes every property in T read-only. Attempting to reassign any property causes a compile-time error.

typescript
interface AppConfig {
  apiUrl: string;
  maxRetries: number;
  timeout: number;
}

const config: Readonly<AppConfig> = {
  apiUrl: 'https://api.example.com',
  maxRetries: 3,
  timeout: 5000,
};

config.apiUrl = 'https://other.com';
// Error: Cannot assign to 'apiUrl' because it is a read-only property

// Readonly also works with arrays
type ReadonlyNumbers = Readonly<number[]>;
// or: ReadonlyArray<number>

const nums: ReadonlyNumbers = [1, 2, 3];
nums.push(4);  // Error: Property 'push' does not exist on type 'readonly number[]'

Custom Mapped Types

All built-in utility types are implemented using mapped types. Understanding mapped types lets you create your own utility types tailored to your codebase.

A mapped type iterates over the keys of a type and transforms each property.

typescript — Mapped Type Syntax
type MappedType<T> = {
  [K in keyof T]: TransformedType
};

// keyof T  = union of all keys in T
// K in     = iterate over each key
// T[K]     = the type of the property at key K

Practical Custom Utility Types

typescript
// Make all properties nullable
type Nullable<T> = {
  [K in keyof T]: T[K] | null;
};

// Make specific properties optional, keep the rest required
type Optional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;

// Usage:
type UserWithOptionalAvatar = Optional<User, 'avatar' | 'bio'>;

// Make specific properties required, keep the rest as-is
type Require<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>;

// Deeply make all properties readonly (recursive)
type DeepReadonly<T> = {
  readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K];
};

// Deeply make all properties partial (recursive)
type DeepPartial<T> = {
  [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
};
typescript — Key Remapping (TypeScript 4.1+)
// Prefix all keys with "on"
type EventHandlers<T> = {
  [K in keyof T as `on${Capitalize<string & K>}`]: (value: T[K]) => void;
};

interface UserFields {
  name: string;
  email: string;
  age: number;
}

type UserEvents = EventHandlers<UserFields>;
// Result:
// {
//   onName: (value: string) => void;
//   onEmail: (value: string) => void;
//   onAge: (value: number) => void;
// }

// Getters for all properties
type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};

type UserGetters = Getters<UserFields>;
// Result:
// {
//   getName: () => string;
//   getEmail: () => string;
//   getAge: () => number;
// }

Conditional Types

Conditional types use the syntax T extends U ? X : Y. If T is assignable to U, the type resolves to X; otherwise Y. They are the if/else of the type system.

typescript
// Basic conditional
type IsString<T> = T extends string ? true : false;

type A = IsString<string>;   // true
type B = IsString<number>;   // false

// Unwrap arrays
type Flatten<T> = T extends Array<infer Item> ? Item : T;

type Str = Flatten<string[]>;   // string
type Num = Flatten<number>;     // number (not an array, returns T)

// Unwrap Promises
type UnwrapPromise<T> = T extends Promise<infer R> ? R : T;

type Result = UnwrapPromise<Promise<string>>;  // string

The infer Keyword

infer declares a type variable within a conditional type. It lets you "capture" part of a type for use in the true branch.

typescript
// Extract the element type from an array
type ElementOf<T> = T extends (infer E)[] ? E : never;

type A = ElementOf<string[]>;     // string
type B = ElementOf<[number, string]>; // number | string

// Extract the first argument of a function
type FirstArg<T> = T extends (first: infer F, ...rest: any[]) => any ? F : never;

type C = FirstArg<(name: string, age: number) => void>; // string

// Extract the props type from a React component
type PropsOf<T> = T extends React.ComponentType<infer P> ? P : never;

To quickly check how your types resolve, paste them into the TypeScript Playground and hover over the type aliases to see the resolved type.

Template Literal Types

Template literal types use backtick syntax at the type level to create string literal types from other string types. Added in TypeScript 4.1.

typescript
// Basic template literal
type Greeting = `Hello, ${string}`;

const a: Greeting = "Hello, World";   // OK
const b: Greeting = "Goodbye, World"; // Error

// Combine with unions to generate all permutations
type Color = 'red' | 'green' | 'blue';
type Size = 'sm' | 'md' | 'lg';

type ClassName = `${Color}-${Size}`;
// Result: 'red-sm' | 'red-md' | 'red-lg'
//       | 'green-sm' | 'green-md' | 'green-lg'
//       | 'blue-sm' | 'blue-md' | 'blue-lg'

Built-In String Manipulation Types

typescript
type Upper = Uppercase<'hello'>;        // 'HELLO'
type Lower = Lowercase<'HELLO'>;        // 'hello'
type Cap = Capitalize<'hello'>;          // 'Hello'
type Uncap = Uncapitalize<'Hello'>;      // 'hello'

// Combine with mapped types
type CSSVars<T extends string> = `--${Lowercase<T>}`;
type ThemeVars = CSSVars<'Primary' | 'Accent' | 'Background'>;
// Result: '--primary' | '--accent' | '--background'

Typed Event System

typescript — Template Literal Types for Event Emitters
interface EventMap {
  user: { id: string; name: string };
  order: { id: string; total: number };
  payment: { id: string; amount: number; currency: string };
}

type EventName = keyof EventMap;

// Generate event method names
type OnEvent = `on${Capitalize<EventName>}`;
// Result: 'onUser' | 'onOrder' | 'onPayment'

// Typed event emitter
interface TypedEmitter {
  on<E extends EventName>(event: E, handler: (data: EventMap[E]) => void): void;
  emit<E extends EventName>(event: E, data: EventMap[E]): void;
}

// Usage: fully typed events
const emitter: TypedEmitter = createEmitter();

emitter.on('user', (data) => {
  console.log(data.name);  // TypeScript knows data is { id: string; name: string }
});

emitter.emit('payment', { id: '1', amount: 99, currency: 'USD' });
emitter.emit('payment', { id: '1', total: 99 });
// Error: 'total' does not exist, expected 'amount'

Real-World Patterns

API Response Wrapper

typescript
// Generic API response type
type ApiResponse<T> = {
  data: T;
  status: number;
  timestamp: string;
};

type ApiError = {
  error: string;
  code: number;
  details?: Record<string, string[]>;
};

type ApiResult<T> = ApiResponse<T> | ApiError;

// Type guard
function isError<T>(result: ApiResult<T>): result is ApiError {
  return 'error' in result;
}

// Usage
async function getUser(id: string): Promise<ApiResult<UserPublic>> {
  const res = await fetch(`/api/users/${id}`);
  return res.json();
}

const result = await getUser('123');
if (isError(result)) {
  console.error(result.error);  // TypeScript narrows to ApiError
} else {
  console.log(result.data.name); // TypeScript narrows to ApiResponse<UserPublic>
}

Form Validation Types

typescript
// Generate validation error types from a form shape
type ValidationErrors<T> = {
  [K in keyof T]?: string[];
};

interface SignupForm {
  username: string;
  email: string;
  password: string;
  confirmPassword: string;
}

type SignupErrors = ValidationErrors<SignupForm>;
// Result:
// {
//   username?: string[];
//   email?: string[];
//   password?: string[];
//   confirmPassword?: string[];
// }

function validate(form: SignupForm): SignupErrors {
  const errors: SignupErrors = {};

  if (form.username.length < 3) {
    errors.username = ['Username must be at least 3 characters'];
  }
  if (form.password !== form.confirmPassword) {
    errors.confirmPassword = ['Passwords do not match'];
  }

  return errors;
}

Builder Pattern with Chained Types

typescript
// Track which fields have been set at the type level
type QueryBuilder<T, Set extends keyof T = never> = {
  select<K extends keyof T>(field: K): QueryBuilder<T, Set | K>;
  where(condition: Partial<Pick<T, Set>>): QueryBuilder<T, Set>;
  execute(): Pick<T, Set>[];
};

// TypeScript tracks which fields you have selected,
// and only allows where() on those fields.
const results = db
  .select('name')
  .select('email')
  .where({ name: 'Alice' })  // OK: 'name' is selected
  .execute();
  // Returns: { name: string; email: string }[]

When building complex TypeScript types, it helps to format and compare your JSON data structures. Use the JSON Formatter to clean up API response samples, or the Diff Checker to compare type definitions side by side.

Write and Test TypeScript Instantly

QTool's TypeScript Playground runs in your browser. Test utility types, check inferred types, and experiment with generics — no setup required.

Open TypeScript Playground JSON to TypeScript

TypeScript Tools


Frequently Asked Questions

TypeScript utility types are built-in generic types that transform existing types into new ones. Instead of writing repetitive type definitions, you can use utility types like Partial<T> (makes all properties optional), Pick<T, K> (selects specific properties), Omit<T, K> (removes specific properties), and Record<K, V> (creates a type with specified keys and value type). They are built into TypeScript and require no imports. Utility types reduce boilerplate, improve code maintainability, and ensure type safety when you need variations of existing types, such as making fields optional for update operations or selecting a subset of properties for API responses.

Pick<T, K> creates a new type by selecting specific properties from type T. Omit<T, K> creates a new type by removing specific properties from type T. They are inverse operations. Use Pick when you want to explicitly list the properties you need (whitelist approach). Use Omit when you want to exclude a few properties from a large type (blacklist approach). For example, if User has 10 properties and you need only name and email, use Pick<User, 'name' | 'email'>. If you need everything except password, use Omit<User, 'password'>. A practical guideline: if you are selecting fewer than half the properties, use Pick. If you are removing fewer than half, use Omit.

Custom utility types are created using mapped types and conditional types. A mapped type iterates over keys of a type and transforms each property. For example, type Nullable<T> = { [K in keyof T]: T[K] | null } makes every property nullable. Conditional types use the syntax T extends U ? X : Y to branch based on type relationships. You can combine them: type Optional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>> makes only specific properties optional while keeping the rest required. Start by understanding keyof (gets all keys as a union), in (iterates over a union), and extends (conditional check). These three keywords are the building blocks of all custom utility types.

Use Record<K, V> when you need a dictionary-like type where all keys have the same value type and the keys come from a specific set. For example, Record<string, number> is a dictionary mapping any string to a number. Record<'draft' | 'published' | 'archived', Article[]> maps three specific status strings to arrays of articles. Use a regular object type (interface or type literal) when properties have different types or when you want to document individual properties. Record is ideal for lookup tables, configuration objects, grouped data, and maps where every value has the same shape. Regular interfaces are better for domain models with distinct fields like User { name: string; age: number; email: string }.

Exclude<T, U> removes members from a union type T that are assignable to U. Extract<T, U> keeps only members from a union type T that are assignable to U. They are opposites. For example, given type Status = 'active' | 'inactive' | 'pending' | 'deleted', Exclude<Status, 'deleted'> gives 'active' | 'inactive' | 'pending' (removes deleted), while Extract<Status, 'active' | 'pending'> gives 'active' | 'pending' (keeps only those two). Exclude is commonly used to remove null or undefined from unions: Exclude<string | null | undefined, null | undefined> gives string. Extract is useful when you want to filter a union to only the types that match a pattern.

Template literal types use the same backtick syntax as JavaScript template literals but at the type level. They create new string literal types by combining existing ones. For example, type Greeting = `hello ${string}` matches any string that starts with 'hello '. Combined with union types, they generate all permutations: type Color = 'red' | 'blue'; type Size = 'sm' | 'lg'; type ClassName = `${Color}-${Size}` produces 'red-sm' | 'red-lg' | 'blue-sm' | 'blue-lg'. TypeScript also provides built-in template literal utility types: Uppercase<T>, Lowercase<T>, Capitalize<T>, and Uncapitalize<T>. Template literal types are powerful for typing CSS class names, API routes, event names, and any string pattern that follows a convention.

NT

Christian Bucher

We build free developer tools including TypeScript playgrounds, JSON converters, and code formatters. 269 tool pages, all browser-based, no signup required.

269 Developer Tools, One Place

Browse 269 indexed tool pages with no QTool account required, and inspect the source on GitHub.

Open Source — Free Forever Try Free Tools

Related Tools

CSS Box Shadow Generator · Emoji Picker & Search · Free JSON to YAML Converter

Related Tools

Free JSON to YAML Converter · YAML to JSON Converter · Free API Mock Server

Related Articles

Built by Miguel

Need a custom tool or website?

From . Delivered in 24-48h. You own the code.

View Services →