Built-in Utility Types
TypeScript provides utility types that transform existing types. These are essential for flexible APIs and pattern implementations.
Partial
Makes all properties optional:
typescript1interface Shipment {2 id: string;3 origin: Address;4 destination: Address;5 weight: number;6 status: string;7}89// All properties become optional10type ShipmentUpdate = Partial<Shipment>;1112// Equivalent to:13// {14// id?: string;15// origin?: Address;16// destination?: Address;17// weight?: number;18// status?: string;19// }2021function updateShipment(id: string, updates: Partial<Shipment>): Shipment {22 const current = getShipment(id);23 return { ...current, ...updates };24}2526// Only update what's needed27updateShipment('SHP001', { status: 'delivered' });28updateShipment('SHP002', { weight: 15, status: 'in_transit' });
Required
Makes all properties required (opposite of Partial):
typescript1interface Config {2 apiKey?: string;3 timeout?: number;4 retries?: number;5}67// All properties become required8type RequiredConfig = Required<Config>;910function initializeClient(config: RequiredConfig): Client {11 // All properties guaranteed to exist12 console.log(config.apiKey); // string, not string | undefined13 console.log(config.timeout); // number, not number | undefined14}
Pick<T, Keys>
Select specific properties:
typescript1interface Package {2 id: string;3 weight: number;4 dimensions: Dimensions;5 fragile: boolean;6 value: number;7 description: string;8}910// Only these properties11type PackageSummary = Pick<Package, 'id' | 'weight' | 'description'>;1213// Equivalent to:14// {15// id: string;16// weight: number;17// description: string;18// }1920function displaySummary(pkg: PackageSummary): void {21 console.log(`${pkg.id}: ${pkg.description} (${pkg.weight}kg)`);22}
Omit<T, Keys>
Exclude specific properties:
typescript1interface User {2 id: string;3 email: string;4 password: string;5 name: string;6 role: string;7}89// Everything except password10type PublicUser = Omit<User, 'password'>;1112// Exclude multiple13type UserPreview = Omit<User, 'password' | 'email'>;1415function getPublicProfile(user: User): PublicUser {16 const { password, ...publicData } = user;17 return publicData;18}
Record<Keys, Type>
Create an object type with specific keys and value type:
typescript1// All carriers have Rate type2type Carrier = 'usps' | 'fedex' | 'ups';34interface Rate {5 base: number;6 perKg: number;7}89type CarrierRates = Record<Carrier, Rate>;1011const rates: CarrierRates = {12 usps: { base: 5.99, perKg: 0.50 },13 fedex: { base: 8.99, perKg: 0.75 },14 ups: { base: 7.99, perKg: 0.65 }15};1617// Dynamic keys18type StatusCounts = Record<string, number>;1920const counts: StatusCounts = {21 pending: 10,22 shipped: 25,23 delivered: 10024};
Readonly
Makes all properties readonly:
typescript1interface Order {2 id: string;3 items: string[];4 total: number;5}67type ImmutableOrder = Readonly<Order>;89const order: ImmutableOrder = {10 id: 'ORD001',11 items: ['item1', 'item2'],12 total: 99.9913};1415// order.id = 'new-id'; // Error: readonly16// order.total = 50; // Error: readonly17// order.items.push('item3'); // Still works! Only shallow readonly
ReturnType
Extract return type of a function:
typescript1function calculateShipping(weight: number, zone: string): {2 rate: number;3 estimatedDays: number;4 carrier: string;5} {6 // Implementation7 return { rate: 25, estimatedDays: 3, carrier: 'fedex' };8}910// Extract the return type11type ShippingResult = ReturnType<typeof calculateShipping>;1213// Now we can use it elsewhere14function processResult(result: ShippingResult): void {15 console.log(`${result.carrier}: $${result.rate}`);16}
Parameters
Extract parameter types as tuple:
typescript1function createShipment(2 origin: Address,3 destination: Address,4 weight: number,5 options?: ShippingOptions6): Shipment {7 // Implementation8}910type CreateShipmentParams = Parameters<typeof createShipment>;11// [Address, Address, number, ShippingOptions?]1213// Useful for wrapper functions14function createShipmentWithLogging(...args: CreateShipmentParams): Shipment {15 console.log('Creating shipment:', args[0], '->', args[1]);16 return createShipment(...args);17}
NonNullable
Removes null and undefined:
typescript1type MaybeAddress = Address | null | undefined;23type DefiniteAddress = NonNullable<MaybeAddress>;4// Address (null and undefined removed)56function requireAddress(addr: MaybeAddress): DefiniteAddress {7 if (!addr) {8 throw new Error('Address required');9 }10 return addr; // Now it's NonNullable11}
Exclude<T, U> and Extract<T, U>
Filter union types:
typescript1type Status = 'pending' | 'processing' | 'shipped' | 'delivered' | 'cancelled';23// Remove specific values4type ActiveStatus = Exclude<Status, 'cancelled' | 'delivered'>;5// 'pending' | 'processing' | 'shipped'67// Keep only specific values8type FinalStatus = Extract<Status, 'delivered' | 'cancelled'>;9// 'delivered' | 'cancelled'
Combining Utility Types
Utility types compose well:
typescript1interface Shipment {2 id: string;3 origin: Address;4 destination: Address;5 weight: number;6 status: Status;7 createdAt: Date;8 updatedAt: Date;9}1011// Partial update excluding id and timestamps12type ShipmentUpdate = Partial<Omit<Shipment, 'id' | 'createdAt'>>;1314// Required fields for creation (no id yet)15type CreateShipmentInput = Required<Omit<Shipment, 'id' | 'createdAt' | 'updatedAt'>>;1617// Readonly summary18type ShipmentSnapshot = Readonly<Pick<Shipment, 'id' | 'status' | 'createdAt'>>;
Summary
| Utility | Purpose | Common Use |
|---|---|---|
Partial<T> | All optional | Update/patch operations |
Required<T> | All required | Strict config validation |
Pick<T, K> | Select properties | DTOs, summaries |
Omit<T, K> | Exclude properties | Remove sensitive data |
Record<K, T> | Key-value mapping | Lookup tables |
Readonly<T> | Immutable | State snapshots |
ReturnType<T> | Function return | Type inference |
Parameters<T> | Function params | Wrapper functions |
NonNullable<T> | Remove null/undefined | Validation |
Exclude<T, U> | Filter out types | Narrow unions |
Extract<T, U> | Keep types | Filter unions |
Next: Create custom mapped and conditional types!