DEV Community

Cover image for TypeScript Utility Types: A Complete Guide
Umesh Malik
Umesh Malik

Posted on • Edited on • Originally published at umesh-malik.com

TypeScript Utility Types: A Complete Guide

TypeScript's built-in utility types are powerful tools that help you write cleaner, more maintainable code. In this guide, I'll walk through the most commonly used utility types with practical examples from real-world applications.

',
description: 'Perfect for update flows where callers only send the fields they want to change.',
bullets: ['Form patches', 'Update DTOs', 'Feature-flagged config overrides'],
tone: 'success'
},
{
eyebrow: 'STRICTNESS',
title: '
Required',
description: 'Useful when a loose input shape becomes a guaranteed runtime shape after defaults or validation.',
bullets: ['Normalized config', 'Post-validation objects', 'Internal invariants'],
tone: 'info'
},
{
eyebrow: 'SHAPING',
title: '
PickandOmit',
description: 'The fastest way to carve focused view models and payload types out of larger domain types.',
bullets: ['Preview cards', 'Create/update payloads', 'Public vs internal fields'],
tone: 'warning'
},
{
eyebrow: 'MAPS',
title: '
Record',
description: 'Great for dictionaries, lookup tables, and keyed collections where the value shape is consistent.',
bullets: ['Role maps', 'Route registries', 'Status-to-label maps'],
tone: 'violet'
},
{
eyebrow: 'SAFETY',
title: '
ReadonlyandNonNullable',
description: 'These types help lock down accidental mutation and strip nullable cases after checks or normalization.',
bullets: ['Immutable config objects', 'Derived non-null props', 'Safer shared state'],
tone: 'success'
},
{
eyebrow: 'INFERENCE',
title: '
ReturnTypeandAwaited`',
description: 'Use them to extract shapes from real functions instead of manually duplicating types that will drift.',
bullets: ['Async loader results', 'Factory output types', 'API wrapper return values'],
tone: 'info'
}
]}
/>

Why Utility Types Matter

When building large-scale applications like the ones I work on at Expedia Group, type safety isn't just nice to have — it's essential. Utility types help you derive new types from existing ones without duplication.

Partial<T>

Makes all properties of T optional. This is incredibly useful for update functions.

`typescript
interface User {
id: string;
name: string;
email: string;
role: 'admin' | 'user';
}

function updateUser(id: string, updates: Partial) {
// Only update the fields that were provided
}

updateUser('123', { name: 'Umesh' }); // Valid!
`

Required<T>

The opposite of Partial — makes all properties required.

`typescript
interface Config {
host?: string;
port?: number;
debug?: boolean;
}

const defaultConfig: Required = {
host: 'localhost',
port: 3000,
debug: false,
};
`

Pick<T, K>

Creates a type with only the specified properties.

`typescript
type UserPreview = Pick;

// Equivalent to:
// { id: string; name: string }
`

Omit<T, K>

Creates a type excluding the specified properties.

`typescript
type CreateUserInput = Omit;

// Everything except id
`

Record<K, T>

Creates a type with keys of type K and values of type T.

`typescript
type UserRoles = Record;

const roleMap: UserRoles = {
admin: [/* admin users /],
editor: [/
editor users */],
};
`

Practical Example: API Response Types

Here's how I combine these utility types in real projects:

`typescript
interface ApiResponse {
data: T;
status: number;
message: string;
}

type UserListResponse = ApiResponse[]>;
type UserUpdatePayload = Partial>;
`

A Few More Utility Types Worth Knowing

Readonly<T>

Use Readonly when an object should never be mutated after creation.

`typescript
interface FeatureFlags {
newSearch: boolean;
redesignedCheckout: boolean;
}

const flags: Readonly = {
newSearch: true,
redesignedCheckout: false,
};
`

NonNullable<T>

Strip null and undefined once you've validated a value.

typescript
type MaybeUser = User | null | undefined;
type SafeUser = NonNullable;

ReturnType<T> and Awaited<T>

Infer the result shape from real functions instead of duplicating it by hand.

`typescript
async function fetchCurrentUser() {
return { id: '123', name: 'Umesh', role: 'admin' as const };
}

type FetchCurrentUserResult = Awaited>;
`

Key Takeaways

  • Use Partial for update operations where not all fields are required
  • Use Pick and Omit to create focused types from larger interfaces
  • Use Record for dictionary-like structures
  • Readonly, NonNullable, ReturnType, and Awaited cover a lot of everyday type work
  • Combine utility types for complex transformations
  • These types are zero-cost at runtime — they only exist during compilation

Mastering these utility types will significantly improve your TypeScript code quality and developer experience.


Originally published at umesh-malik.com

Top comments (0)