Skip to content

TypeScript Utility Types — Complete Reference Guide

DodaTech Updated 2026-06-28 8 min read

In this tutorial, you will learn about TypeScript Utility Types. We cover key concepts, practical examples, and best practices to help you master this topic.

TypeScript's built-in utility types are generic type transformations that ship with the compiler — they handle common patterns like excluding null, extracting function return types, making properties optional, and unwrapping promises, saving you from writing complex conditional types by hand.

What You'll Learn

  • Omit, Pick — object key selection
  • Extract, Exclude — union filtering
  • NonNullable — null/undefined removal
  • ReturnType, Parameters — function type extraction
  • Awaited — promise unwrapping
  • ThisType, ThisParameterType — context typing

Why It Matters

Utility types reduce boilerplate. Instead of writing a conditional type to extract a function's return type, you just write ReturnType<typeof myFunction>. Knowing the utility types by heart makes you dramatically more productive and your code more readable.

Real-World Use

Durga Antivirus Pro uses Omit<ThreatReport, "internalNotes"> before sending reports to the client API. ReturnType<typeof createApiHandler> dynamically types the response shape. NonNullable<User | null> ensures authentication checks are complete before accessing user data.

Learning Path

flowchart LR
  A[Mapped Types] --> B[Utility Types]
  B --> C[Namespaces & Modules]
  B --> D[You Are Here]
  C --> E[Declaration Files]
  E --> F[Classes & OOP]

Object Type Utilities

Partial<T>

Makes all properties optional:

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

const partialUser: Partial<User> = { name: "Alice" };
// { name?: string; age?: number; email?: string }

Required<T>

Makes all properties required (removes ?):

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

const config: Required<Config> = { host: "localhost", port: 3000 };
// Both properties are now required

Readonly<T>

Makes all properties readonly:

interface Point { x: number; y: number; }
const point: Readonly<Point> = { x: 10, y: 20 };
// point.x = 5; // Error

Pick<T, K>

Selects a subset of properties:

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

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

Omit<T, K>

Removes properties:

type UserWithoutPassword = Omit<User, "password" | "ssn">;
// { id: string; name: string; email: string }

Record<K, T>

Creates an object type with specified keys and value type:

type PageNames = "home" | "about" | "contact";
type PageMap = Record<PageNames, { title: string; path: string }>;

const pages: PageMap = {
  home: { title: "Home", path: "/" },
  about: { title: "About", path: "/about" },
  contact: { title: "Contact", path: "/contact" },
};

Union Type Utilities

Extract<T, U>

Extracts members of T that are assignable to U:

type Colors = "red" | "green" | "blue" | "yellow";
type WarmColors = Extract<Colors, "red" | "yellow">;
// "red" | "yellow"

type Values = string | number | boolean;
type Numbers = Extract<Values, number>;
// number

Exclude<T, U>

Removes members of T that are assignable to U:

type Colors = "red" | "green" | "blue" | "yellow";
type CoolColors = Exclude<Colors, "red" | "yellow">;
// "green" | "blue"

type Values = string | number | boolean | null;
type NonNullValues = Exclude<Values, null | undefined>;
// string | number | boolean

NonNullable<T>

Removes null and undefined from T:

type Value = string | number | null | undefined;
type Clean = NonNullable<Value>;
// string | number

Function Type Utilities

ReturnType<T>

Extracts the return type of a function type:

function createUser() {
  return { id: "123", name: "Alice" };
}

type UserResult = ReturnType<typeof createUser>;
// { id: string; name: string }

type Fn = (x: number) => string;
type R = ReturnType<Fn>;
// string

Parameters<T>

Extracts the parameter types as a tuple:

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

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

function log(...args: unknown[]): void {}
type LogParams = Parameters<typeof log>;
// unknown[]

ConstructorParameters<T>

Extracts parameter types of a constructor:

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

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

InstanceType<T>

Extracts the instance type from a constructor type:

type UserInstance = InstanceType<typeof User>;
// User (the class instance type)

Promise Utilities

Awaited<T> (TS 4.5+)

Unwraps promises recursively:

type P1 = Awaited<Promise<string>>;           // string
type P2 = Awaited<Promise<Promise<number>>>;  // number
type P3 = Awaited<number>;                    // number (not a promise)
type P4 = Awaited<Promise<string | number>>;  // string | number

Using Awaited with async functions

async function fetchUser(): Promise<{ name: string }> {
  return { name: "Alice" };
}

type UserData = Awaited<ReturnType<typeof fetchUser>>;
// { name: string }

String Utilities (TS 4.1+)

type Upper = Uppercase<"hello">;       // "HELLO"
type Lower = Lowercase<"HELLO">;       // "hello"
type Capital = Capitalize<"hello">;    // "Hello"
type Uncapital = Uncapitalize<"Hello">; // "hello"

These work with unions too:

type EventName = "click" | "submit";
type Handler = `on${Capitalize<EventName>}`;
// "onClick" | "onSubmit"

ThisType and ThisParameterType

ThisType<T>

Marks the this context for an object literal (used in Vue.js, Vuex, etc.):

type ObjectDescriptor<D, M> = {
  data?: D;
  methods?: M & ThisType<D & M>;
};

function createApp<D, M>(desc: ObjectDescriptor<D, M>): D & M {
  return { ...desc.data, ...desc.methods } as any;
}

const app = createApp({
  data() { return { count: 0 }; },
  methods: {
    increment() {
      this.count++; // this is typed as D & M — has count and increment
    },
  },
});

ThisParameterType<T>

Extracts the this parameter type from a function:

function onClick(this: HTMLButtonElement, event: MouseEvent): void {}

type ThisType = ThisParameterType<typeof onClick>;
// HTMLButtonElement

OmitThisParameter<T>

Removes the this parameter from a function type:

type CleanFn = OmitThisParameter<typeof onClick>;
// (event: MouseEvent) => void (no this parameter)

Common Mistakes

1. Using Omit When Pick Is More Precise

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

// Both work, but Pick is safer if new properties are added
type A = Omit<User, "age" | "email">;  // { name: string }
type B = Pick<User, "name">;           // { name: string }

Pick fails when a key doesn't exist. Omit silently ignores missing keys, which can mask bugs.

2. Forgetting ReturnType Only Works with Function Types

function fn() { return 42; }
// type R = ReturnType<fn>; // Error: 'fn' refers to a value, not a type
type R = ReturnType<typeof fn>; // Correct: number

3. Using Extract Instead of Pick for Objects

// Extract works on unions, Pick works on objects
type Union = "a" | "b" | "c";
type E = Extract<Union, "a" | "b">; // "a" | "b" ✓

interface Obj { a: number; b: string; c: boolean; }
// type E2 = Extract<Obj, "a" | "b">; // never — Extract for unions
type P = Pick<Obj, "a" | "b">; // { a: number; b: string } ✓

4. Using Awaited Before TS 4.5

Before TypeScript 4.5, use nested conditional types:

type MyAwaited<T> = T extends Promise<infer U> ? MyAwaited<U> : T;

5. Over-Nesting Utility Types

// Hard to read
type Complex = Partial<Omit<Record<string, Required<Pick<User, "name">>>, ...>>;

// Better: intermediate types
type UserName = Pick<User, "name">;
type NameRecord = Record<string, Required<UserName>>;
type Config = Partial<Omit<NameRecord, "admin">>;

Practice Questions

  1. What is the difference between Omit<T, K> and Exclude<T, K>? Omit works on object types (removes properties). Exclude works on union types (removes members).

  2. What does ReturnType<typeof fn> give you? The return type of function fn as a type.

  3. How do you make all properties of a type deeply unwrapped from promises? Use Awaited<T> recursively: type DeepAwaited<T> = T extends Promise<infer U> ? DeepAwaited<U> : T;

  4. What is NonNullable used for? Removing null and undefined from a union type.

Challenge: Use utility types to create a function that takes an async API function, calls it, and returns the fully unwrapped result type. Use ReturnType, Awaited, and Parameters.

FAQ

Can I combine utility types?

Yes, chaining is common: Partial<Omit<User, "id">> makes all properties optional except id.

Are utility types available in all TypeScript versions?

Most utility types were added in TS 2.1-2.8. Awaited was added in TS 4.5. Check your tsconfig target.

Do utility types work with classes?

Yes. Partial<MyClass> creates a type with all class properties as optional.

What is the difference between `Pick` and `Extract`?

Pick selects properties from object types. Extract selects members from union types.

Can I create my own utility types?

Yes. All utility types are implemented as generic types in TypeScript's standard library. You can create custom ones like DeepPartial, Mutable, etc.

Mini Project: Type-Safe API Response Transformer

// src/api-transform.ts

interface APIResponse<T> {
  data: T;
  meta: {
    requestId: string;
    timestamp: number;
  };
}

interface UserRaw {
  id: string;
  name: string;
  email: string;
  password_hash: string;
  internal_notes: string;
  created_at: string;
}

interface ScanRaw {
  id: string;
  file_name: string;
  threats: string[];
  scan_duration_ms: number;
}

// Transform raw API data to client-safe format
type ClientUser = Omit<UserRaw, "password_hash" | "internal_notes"> & {
  createdAt: Date;
};

type ClientScan = Omit<ScanRaw, "scan_duration_ms"> & {
  scanDuration: number;
  completedAt: Date;
};

type ClientResponse<T, R> = Omit<APIResponse<T>, "data"> & {
  data: R;
};

function transformUser(raw: UserRaw): ClientUser {
  return {
    id: raw.id,
    name: raw.name,
    email: raw.email,
    created_at: raw.created_at,
    createdAt: new Date(raw.created_at),
  };
}

function transformScan(raw: ScanRaw): ClientScan {
  return {
    id: raw.id,
    file_name: raw.file_name,
    threats: raw.threats,
    scanDuration: raw.scan_duration_ms,
    completedAt: new Date(),
  };
}

async function fetchUsers(): Promise<ClientResponse<UserRaw, ClientUser[]>> {
  const response: APIResponse<UserRaw[]> = await fetch("/api/users").then(r => r.json());
  return {
    ...response,
    data: response.data.map(transformUser),
  };
}

console.log("Utility types demo:");
type Demo = {
  partial: Partial<UserRaw>;
  picked: Pick<UserRaw, "id" | "name">;
  omitted: Omit<UserRaw, "password_hash">;
  nonNull: NonNullable<string | null | undefined>; // string
  returnType: ReturnType<typeof transformUser>;
  awaited: Awaited<ReturnType<typeof fetchUsers>>;
};

console.log("Type safety achieved.");

What's Next

Now explore namespaces and modules:

Lesson Description
{{< ref "/programming-languages/typescript/14-mapped-types" >}} Review mapped types
{{< ref "/programming-languages/typescript/16-namespaces-modules" >}} Namespace vs module, ES modules
{{< ref "/programming-languages/typescript/17-declaration-files" >}} Declaration files and DefinitelyTyped

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro