Utility types are built-in TypeScript tools for transforming, modifying, or deriving types based on existing ones. The most commonly encountered:
Partial<T> — makes all properties of type T optionalRequired<T> — makes all properties mandatoryPick<T, K> — selects properties K from type T onlyOmit<T, K> — excludes properties K from type Ttype User = { id: number; name: string; age?: number }; type UserPreview = Pick<User, 'id' | 'name'>; type UserPatch = Partial<User>; type FullUser = Required<User>; type UserWithoutAge = Omit<User, 'age'>;
This allows for creating more flexible APIs and DTOs.
If a property of the type is specified as optional (
age?: number), how willRequired<T>behave? Will it remain optional?
Answer: No, Required<T> transforms all properties of type T, including optional ones, into mandatory (required).
type User = { age?: number }; type UserRequired = Required<User>; // UserRequired: { age: number }
Story
Used Partial<T> for updating objects, thinking it was automatically safe for the database. Forgot to check fields for null/undefined and faced validation errors when updating entities.
Story
Used Omit<T, K> to exclude technical properties from the request type, not considering that during model refactoring new technical fields were not added to the exclusion list. As a result, private fields "leaked" through the API.
Story
In the Shared library, created type Pick<T, K>, making a mistake in the list of keys: K was partially missing from the original type. TypeScript missed the error when using literal strings, but when the original type changed, there was a mismatch, and part of the types "broke" across all services.