DEV Community

Rizwan Saleem
Rizwan Saleem

Posted on

Advanced TypeScript patterns: generics, conditional types, and template literals

Advanced TypeScript patterns: generics, conditional types, and template literals

TypeScript's type system is remarkably powerful. Beyond basic types and interfaces, TypeScript provides advanced patterns that let you model complex data structures and enforce runtime invariants at compile time. These patterns enable type-safe abstractions that catch bugs before they reach production.

Generic constraints with extends give you type-safe abstractions. Instead of using any or unknown, constrain generic parameters to specific shapes. A function that sorts entities should accept T extends { id: string } rather than any. Constraints make generics more useful and easier to understand.

Conditional types branch on type relationships. T extends string ? 'string type' : 'other type' lets you create types that depend on other types. The Extract and Exclude utility types use conditional types. You can build complex type transformations that adapt to the input type.

Template literal types create string types from other types. get${Capitalize<K>} defines a pattern for function names. Template literal types are essential for type-safe event emitters, API clients, and configuration objects. They bring type safety to string manipulation.

Mapped types transform object types programmatically. { [K in keyof T]: T[K] | null } creates a type where all properties of T can be null. Partial, Required, and Pick are built-in mapped types. You can create custom mapped types for common transformations in your codebase.

The infer keyword extracts types from other types in conditional types. infer lets you pull the return type from a function type, the element type from an array, or the resolved type from a promise. infer is the most powerful tool in TypeScript's type-level programming toolkit.

Discriminated unions model states that have different shapes. Each member of the union has a discriminant property that TypeScript uses to narrow the type. A fetch result can be { status: 'loading' } | { status: 'success', data: T } | { status: 'error', error: Error }. Discriminated unions make state machines type-safe.

Build a library of utility types for your project. Common ones: DeepPartial, NonNullable, AsyncReturnType, and Brand. Document each utility type with examples. A shared type library accelerates development across the codebase and ensures consistent typing practices.

-

Rizwan Saleem | https://rizwansaleem.co

Top comments (0)