Hint
Combine utility types in function signatures to produce modified shapes — common in config and builder patterns
Generic utility functions compose TypeScript's built-in modifiers to create flexible, type-safe APIs.
// Deep partial — makes all nested properties optional
type DeepPartial<T> = T extends object
? { [K in keyof T]?: DeepPartial<T[K]> }
: T;
function mergeDefaults<T extends object>(
defaults: T,
overrides: DeepPartial<T>
): T {
return { ...defaults, ...overrides } as T;
}
const config = mergeDefaults(
{ host: 'localhost', port: 3000, ssl: false },
{ port: 8080 } // only override port — others keep defaults
);
// Update function — require at least one field
type AtLeastOne<T> = { [K in keyof T]-?: Pick<T, K> & Partial<T> }[keyof T];
function update<T extends object>(id: number, patch: Partial<T>): void {
// ...apply patch
}
// Freeze function — returns readonly version
function freeze<T extends object>(obj: T): Readonly<T> {
return Object.freeze(obj);
}
const settings = freeze({ theme: 'dark', lang: 'en' });
settings.theme = 'light'; // ❌ Error — readonly
Partial<T> for update functions and Required<T> for build/create functions is idiomatic TypeScript. It prevents passing full objects when you only need to patch a few fields.