TypeScript ships a set of built-in generic types that transform an existing type instead of redeclaring a near-duplicate by hand:
interface User { id: number; name: string; email: string; }
type PartialUser = Partial<User>; // every field optional
type RequiredUser = Required<User>; // every field required (opposite of Partial)
type ReadonlyUser = Readonly<User>; // every field readonly
type UserPreview = Pick<User, "id" | "name">; // only id and name
type UserWithoutEmail = Omit<User, "email">; // everything except email
type UserMap = Record<number, User>; // { [id: number]: User }
type MaybeUser = User | undefined;
type DefiniteUser = NonNullable<MaybeUser>; // strips undefined (and null) — back to User
| Utility | What it does |
|---|---|
Partial<T> | Every property becomes optional — ideal for a "patch"-style update function's parameter |
Required<T> | Every property becomes required, even ones declared optional on T |
Readonly<T> | Every property becomes readonly — a compile-time-only immutability guard |
Pick<T, K> | A new type with only the listed keys |
Omit<T, K> | A new type with every key except the listed ones |
Record<K, V> | An object type mapping every key in K to a value of type V |
ReturnType<F> | Extracts a function type's return type, without calling it |
Parameters<F> | Extracts a function type's parameter types as a tuple |
NonNullable<T> | Removes null and undefined from a union |
ReturnType and Parameters are especially useful for deriving a type from an existing function you don't control the source of (a third-party library, a generated client) instead of manually re-typing its signature:
function createUser(name: string, age: number) {
return { id: Date.now(), name, age };
}
type NewUser = ReturnType<typeof createUser>; // { id: number; name: string; age: number }
type CreateUserArgs = Parameters<typeof createUser>; // [name: string, age: number]