Back to Blog
TypeScriptTypesUtilityGenerics

TypeScript Utility Types Guide

Master TypeScript's built-in utility types. From Partial to Required to advanced type transformations.

B
Bootspring Team
Engineering
November 5, 2020
7 min read

TypeScript provides powerful utility types for type transformations. Here's a comprehensive guide.

Property Modifiers#

1// Partial - all properties optional 2interface User { 3 id: number; 4 name: string; 5 email: string; 6} 7 8type PartialUser = Partial<User>; 9// { id?: number; name?: string; email?: string } 10 11function updateUser(id: number, updates: Partial<User>): User { 12 const user = findUser(id); 13 return { ...user, ...updates }; 14} 15 16updateUser(1, { name: 'Alice' }); // Only update name 17 18// Required - all properties required 19interface Config { 20 host?: string; 21 port?: number; 22 ssl?: boolean; 23} 24 25type RequiredConfig = Required<Config>; 26// { host: string; port: number; ssl: boolean } 27 28function initServer(config: RequiredConfig): void { 29 // All properties guaranteed 30} 31 32// Readonly - all properties readonly 33type ReadonlyUser = Readonly<User>; 34// { readonly id: number; readonly name: string; readonly email: string } 35 36const frozenUser: ReadonlyUser = { 37 id: 1, 38 name: 'Alice', 39 email: 'alice@example.com', 40}; 41// frozenUser.name = 'Bob'; // Error: readonly

Property Selection#

1// Pick - select specific properties 2interface Article { 3 id: number; 4 title: string; 5 content: string; 6 author: string; 7 createdAt: Date; 8 updatedAt: Date; 9} 10 11type ArticlePreview = Pick<Article, 'id' | 'title' | 'author'>; 12// { id: number; title: string; author: string } 13 14// Omit - exclude specific properties 15type ArticleInput = Omit<Article, 'id' | 'createdAt' | 'updatedAt'>; 16// { title: string; content: string; author: string } 17 18// Combining Pick and Omit 19type EditableFields = Pick<Article, 'title' | 'content'>; 20type ArticleWithMeta = Omit<Article, 'content'>;

Key Types#

1// Record - create object type with specific keys 2type PageNames = 'home' | 'about' | 'contact'; 3type PageConfig = Record<PageNames, { title: string; path: string }>; 4 5const pages: PageConfig = { 6 home: { title: 'Home', path: '/' }, 7 about: { title: 'About', path: '/about' }, 8 contact: { title: 'Contact', path: '/contact' }, 9}; 10 11// Dynamic keys 12type UserRoles = Record<string, string[]>; 13const permissions: UserRoles = { 14 admin: ['read', 'write', 'delete'], 15 user: ['read'], 16}; 17 18// keyof - get keys as union 19type UserKeys = keyof User; 20// 'id' | 'name' | 'email' 21 22function getValue<T, K extends keyof T>(obj: T, key: K): T[K] { 23 return obj[key]; 24}

Exclusion Types#

1// Exclude - exclude types from union 2type AllTypes = string | number | boolean | null | undefined; 3type PrimitiveTypes = Exclude<AllTypes, null | undefined>; 4// string | number | boolean 5 6type Status = 'pending' | 'active' | 'completed' | 'cancelled'; 7type ActiveStatus = Exclude<Status, 'pending' | 'cancelled'>; 8// 'active' | 'completed' 9 10// Extract - extract matching types 11type TextTypes = Extract<AllTypes, string | number>; 12// string | number 13 14type PendingOrActive = Extract<Status, 'pending' | 'active'>; 15// 'pending' | 'active' 16 17// NonNullable - exclude null and undefined 18type MaybeString = string | null | undefined; 19type DefiniteString = NonNullable<MaybeString>; 20// string

Function Types#

1// Parameters - get function parameter types 2function createUser(name: string, age: number, admin: boolean): User { 3 // ... 4} 5 6type CreateUserParams = Parameters<typeof createUser>; 7// [string, number, boolean] 8 9// ReturnType - get function return type 10type CreateUserReturn = ReturnType<typeof createUser>; 11// User 12 13// Async function return type 14async function fetchUser(id: number): Promise<User> { 15 // ... 16} 17 18type FetchReturn = ReturnType<typeof fetchUser>; 19// Promise<User> 20 21type AwaitedReturn = Awaited<ReturnType<typeof fetchUser>>; 22// User 23 24// ConstructorParameters 25class MyClass { 26 constructor(name: string, value: number) {} 27} 28 29type MyClassParams = ConstructorParameters<typeof MyClass>; 30// [string, number] 31 32// InstanceType 33type MyClassInstance = InstanceType<typeof MyClass>; 34// MyClass

String Manipulation#

1// Uppercase 2type Shouting = Uppercase<'hello'>; 3// 'HELLO' 4 5// Lowercase 6type Quiet = Lowercase<'HELLO'>; 7// 'hello' 8 9// Capitalize 10type Titled = Capitalize<'hello'>; 11// 'Hello' 12 13// Uncapitalize 14type Untitled = Uncapitalize<'Hello'>; 15// 'hello' 16 17// Practical example: event handlers 18type EventName = 'click' | 'focus' | 'blur'; 19type HandlerName = `on${Capitalize<EventName>}`; 20// 'onClick' | 'onFocus' | 'onBlur'

Awaited Type#

1// Unwrap Promise types 2type Promised = Promise<string>; 3type Resolved = Awaited<Promised>; 4// string 5 6// Nested promises 7type NestedPromise = Promise<Promise<number>>; 8type DeepResolved = Awaited<NestedPromise>; 9// number 10 11// With union types 12type MixedPromise = Promise<string> | number; 13type MixedResolved = Awaited<MixedPromise>; 14// string | number 15 16// Practical usage 17async function processData(): Promise<Data> { 18 // ... 19} 20 21type DataType = Awaited<ReturnType<typeof processData>>; 22// Data

ThisType#

1// Define 'this' context for object literals 2interface Methods { 3 greet(): string; 4 getName(): string; 5} 6 7interface Data { 8 name: string; 9} 10 11const obj: Methods & ThisType<Data & Methods> = { 12 greet() { 13 return `Hello, ${this.getName()}`; 14 }, 15 getName() { 16 return this.name; 17 }, 18}; 19 20// Vue-style options 21interface ComponentOptions<D, M> { 22 data(): D; 23 methods: M & ThisType<D & M>; 24} 25 26function defineComponent<D, M>(options: ComponentOptions<D, M>): D & M { 27 // ... 28}

Combining Utility Types#

1// Complex type transformations 2interface Product { 3 id: number; 4 name: string; 5 price: number; 6 description: string; 7 category: string; 8 inStock: boolean; 9} 10 11// Partial update excluding id 12type ProductUpdate = Partial<Omit<Product, 'id'>>; 13 14// Required subset 15type RequiredProductInfo = Required<Pick<Product, 'name' | 'price'>>; 16 17// Readonly response 18type ProductResponse = Readonly<Product>; 19 20// Record of products by id 21type ProductCatalog = Record<string, Product>; 22 23// Pick and make readonly 24type ProductSummary = Readonly<Pick<Product, 'id' | 'name' | 'price'>>;

Custom Utility Types#

1// Deep Partial 2type DeepPartial<T> = T extends object 3 ? { [P in keyof T]?: DeepPartial<T[P]> } 4 : T; 5 6interface NestedConfig { 7 server: { 8 host: string; 9 port: number; 10 ssl: { 11 enabled: boolean; 12 cert: string; 13 }; 14 }; 15} 16 17type PartialConfig = DeepPartial<NestedConfig>; 18 19// Deep Readonly 20type DeepReadonly<T> = T extends object 21 ? { readonly [P in keyof T]: DeepReadonly<T[P]> } 22 : T; 23 24// Mutable (remove readonly) 25type Mutable<T> = { 26 -readonly [P in keyof T]: T[P]; 27}; 28 29// Optional keys 30type OptionalKeys<T> = { 31 [K in keyof T]-?: undefined extends T[K] ? K : never; 32}[keyof T]; 33 34// Required keys 35type RequiredKeys<T> = { 36 [K in keyof T]-?: undefined extends T[K] ? never : K; 37}[keyof T]; 38 39// Function properties only 40type FunctionProperties<T> = { 41 [K in keyof T]: T[K] extends Function ? K : never; 42}[keyof T];

Practical Examples#

1// Form handling 2interface FormFields { 3 username: string; 4 email: string; 5 password: string; 6} 7 8type FormErrors = Partial<Record<keyof FormFields, string>>; 9type FormTouched = Partial<Record<keyof FormFields, boolean>>; 10 11interface FormState { 12 values: FormFields; 13 errors: FormErrors; 14 touched: FormTouched; 15} 16 17// API responses 18interface ApiResponse<T> { 19 data: T; 20 status: number; 21 message: string; 22} 23 24type UserResponse = ApiResponse<User>; 25type UsersResponse = ApiResponse<User[]>; 26 27// Event handling 28type EventHandlers<T> = { 29 [K in keyof T as `on${Capitalize<string & K>}`]: (value: T[K]) => void; 30}; 31 32type UserEvents = EventHandlers<User>; 33// { onId: (value: number) => void; onName: (value: string) => void; ... }

Best Practices#

Selection: ✓ Use Pick for whitelist approach ✓ Use Omit for blacklist approach ✓ Combine for complex transformations ✓ Document type intentions Modifiers: ✓ Use Partial for optional updates ✓ Use Required for strict validation ✓ Use Readonly for immutability ✓ Consider deep versions for nested types Functions: ✓ Use Parameters for argument types ✓ Use ReturnType for return types ✓ Use Awaited for async functions ✓ Create type-safe wrappers Custom Types: ✓ Build on built-in utilities ✓ Use conditional types for flexibility ✓ Document complex type transformations ✓ Test with various input types

Conclusion#

TypeScript's utility types enable powerful type transformations. Master the built-in types for common patterns, combine them for complex transformations, and create custom utilities for project-specific needs. These tools make TypeScript's type system incredibly flexible and expressive.

Share this article

Help spread the word about Bootspring