tezvyn:

TypeScript: Modify Properties with Mapped Type Modifiers

AI-drafted, machine-checkedSource: typescriptlang.orgintermediate

Mapped type modifiers let you add or remove `readonly` and `?` from a type's properties. Use them to create a fully required type from an optional one, or a mutable version of a readonly object. The footgun: remember the `-` prefix to *remove* modifiers.

WHY IT EXISTS To avoid manually creating slight variations of a type. If you have a User type and need a PartialUser for a form or a ReadonlyUser for configuration, you don't want to copy-paste the entire type definition. Mapped types let you derive new types programmatically, keeping your code DRY (Don't Repeat Yourself).

THE MENTAL MODEL Think of mapped type modifiers as a factory for type shapes. You provide an input type and an instruction—like "make everything mutable" or "make everything required"—and it outputs a new type with those changes applied to every property, without you having to touch each one individually.

HOW IT WORKS A mapped type iterates over the keys of a source type. Modifiers use + and - prefixes to add or remove readonly and optional (?) markers. A + or no prefix adds the modifier, while a - prefix removes it. The syntax is +/-readonly [Property in keyof Type] for mutability and [Property in keyof Type]+/-?: for optionality. For example, -readonly makes a property mutable, and -? makes a property required.

WHEN TO USE IT Use modifiers to create common variations of a base type. Three key scenarios are: first, creating a mutable version of a readonly type (e.g., {-readonly [P in keyof T]: T[P]}); second, making all properties of an optional type required (e.g., {[P in keyof T]-?: T[P]}); third, making all properties optional, which is what the built-in Partial<T> utility type does.

WHEN NOT TO USE IT Don't use mapped type modifiers if you only need to change one or two properties; using Pick or Omit with a type intersection (&) is often simpler. Also, if you need to rename the keys themselves, you'd use the as keyword for key remapping, which is a different feature of mapped types.

ONE CANONICAL EXAMPLE Let's make a type with optional properties fully required. Given a type MaybeUser = { id: string; name?: string; age?: number; }, we can create a Concrete utility type: type Concrete<T> = { [P in keyof T]-?: T[P] };. Applying this as type User = Concrete<MaybeUser>; results in a new User type where id, name, and age are all required, non-optional properties.

Read the original → typescriptlang.org

Get five bites like this every day.

Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.