TypeScript Utility Types — Complete Reference Guide
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
What is the difference between
Omit<T, K>andExclude<T, K>?Omitworks on object types (removes properties).Excludeworks on union types (removes members).What does
ReturnType<typeof fn>give you? The return type of functionfnas a type.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;What is
NonNullableused for? Removingnullandundefinedfrom 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
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