TypeScript Utility Types
debt(d5/e3/b3/t5)
Closest to 'specialist tool catches it' (d5). The detection_hints list TypeScript compiler and ESLint as tools. Manual type duplication (e.g. writing PartialUser instead of Partial<User>) is not caught by basic compilation — TypeScript won't error on redundant manual types — but a specialist ESLint plugin (e.g. @typescript-eslint with custom rules) or dedicated type-coverage tooling can flag unnecessary duplication. It won't surface silently in production (ruling out d7/d9), but it's not caught by default linting either.
Closest to 'simple parameterised fix' (e3). The quick_fix describes replacing manual partial/pick type definitions with Partial<T>, Pick<T,K>, Omit<T,K>, or ReturnType<typeof fn>. This is a local refactor — swapping out duplicated type definitions for utility type calls — but may touch multiple interface/type declarations across a file or component, making it slightly more than a one-line patch (e1) but clearly not cross-cutting.
Closest to 'localised tax' (b3). The burden of NOT using utility types is confined to the type definitions themselves. Manually duplicated types impose a maintenance tax whenever the base type changes (every field must be updated in all copies), but this stays largely within the type layer and doesn't spread architectural gravity across the whole codebase. Applies to web and CLI contexts, but the impact is scoped.
Closest to 'notable trap' (t5). The canonical misconception is believing you must manually write partial versions of types, unaware that Partial<User> exists and auto-updates. Additionally, the common_mistakes note that Partial<T> is shallow — Partial does NOT recursively make nested objects partial — which is a documented gotcha many developers eventually learn. These two traps together justify t5: notable but documented surprises rather than catastrophic misbehavior.
Also Known As
TL;DR
Explanation
TypeScript ships with utility types for common transformations: Partial<T> makes all properties optional; Required<T> makes all required; Readonly<T> makes all readonly; Pick<T,K> selects a subset of properties; Omit<T,K> removes properties; Record<K,V> creates an object type with specific key/value types; ReturnType<F> extracts a function's return type; Parameters<F> extracts parameter types. These eliminate the copy-paste type maintenance problem — derive types from a single source of truth.
Diagram
flowchart LR
BASE[User type<br/>id name email role]
BASE -->|Partial| PART[All fields optional<br/>good for update DTOs]
BASE -->|Required| REQ[All fields required]
BASE -->|Readonly| RO[All fields readonly]
BASE -->|Pick name email| PICK[Only name and email]
BASE -->|Omit role| OMIT[All except role]
BASE -->|Record string User| REC[Dictionary of Users]
subgraph Common_Uses
PART -->|patch endpoints| USE1[UpdateUserDto]
PICK -->|public API| USE2[PublicProfile]
RO -->|immutable config| USE3[AppConfig]
end
style BASE fill:#6e40c9,color:#fff
style PART fill:#1f6feb,color:#fff
style PICK fill:#238636,color:#fff
style RO fill:#d29922,color:#fff
Common Misconception
Why It Matters
Common Mistakes
- Manually writing partial or pick types instead of using Partial<T> and Pick<T,K>.
- Using Record<string, any> when a more specific value type is known.
- Not using ReturnType<typeof fn> to infer return types — avoids duplication when the function signature changes.
- Forgetting that utility types are shallow — Partial<T> does not make nested objects partial; use DeepPartial for that.
Code Examples
// Manually duplicated partial type — drifts when User changes:
type User = { id: number; name: string; email: string; role: string };
// Manual copy — must be updated every time User changes:
type UpdateUserDTO = {
name?: string;
email?: string;
role?: string;
};
// Utility types — derived, never drift:
type User = { id: number; name: string; email: string; role: string; password: string };
type UpdateUserDTO = Omit<Partial<User>, 'id' | 'password'>;
// Equivalent to: { name?: string; email?: string; role?: string }
// Automatically includes new User fields, excludes id and password
type UserPublic = Omit<User, 'password'>; // Safe for API responses
type UserKeys = keyof User; // 'id' | 'name' | 'email' | 'role' | 'password'