TypeScript Mapped Types: Reuse and Transform Interface Properties
Derive smaller TypeScript shapes with Pick, make edit fields optional with Partial, and map interface properties to new value types.

If a smaller type should stay tied to an existing interface, start with Pick. It names the properties you want without copying their value types. Reach for a custom mapped type when the relationship is different—for example, when every property needs a corresponding boolean flag.
Select more than one property
Suppose an application has an account shape but needs only contact details for one operation:
interface Account {
id: string;
displayName: string;
email: string;
status: "active" | "suspended";
}
type AccountContact = Pick<Account, "displayName" | "email">;
const contact: AccountContact = {
displayName: "Avery",
email: "[email protected]",
};
The union "displayName" | "email" selects both keys. AccountContact has those two properties, each with the string type declared in Account. It does not include id or status. This is what makes Pick useful for a dependent shape: if a selected property’s type changes in Account, the derived type uses that property type rather than a separately copied declaration. The TypeScript utility-types reference describes the keys accepted by Pick as a string literal or a union of string literals.
For an edit shape where either contact field may be left out, compose Partial with Pick:
type AccountContactEdit = Partial<Pick<Account, "displayName" | "email">>;
const emailOnly: AccountContactEdit = {
email: "[email protected]",
};
const noChanges: AccountContactEdit = {};
In this example, displayName and email can each be omitted; Partial makes the selected properties optional while retaining their declared types. Notice that id is not part of this edit shape. If an operation needs an account identifier, model that requirement separately rather than assuming Partial will retain a required field.
What the mapping reuses
The selection can be written as a small mapped type:
type Select<T, Keys extends keyof T> = {
[K in Keys]: T[K];
};
type SelectedContact = Select<Account, "displayName" | "email">;
// Deliberately invalid: "nickname" is not a key of Account.
// type UnknownSelection = Select<Account, "nickname">;
// Deliberately invalid: email must have Account's string value type.
// const wrongContact: SelectedContact = {
// displayName: "Avery",
// email: 42,
// };
Keys extends keyof T limits the selection to keys available on T. [K in Keys] creates a property for each selected key, while T[K] looks up that property’s type in the source. For this job, the built-in Pick<T, Keys> already expresses the relationship; Select is useful here to show the mechanism, not as another utility a project needs to maintain. The mapped-types handbook explains the same key-iteration pattern.
If you want everything except named properties, Omit expresses that choice instead. Pick is an allow-list: adding a new property to Account will not silently add it to AccountContact. That distinction matters when the smaller type is meant to promise a specific set of fields.
Transform every property instead
Selecting keys and changing value types are separate decisions. Imagine a set of features represented by functions, with a boolean option for each one:
interface Features {
darkMode: () => void;
newProfile: () => void;
}
type OptionsFlags<T> = {
[K in keyof T]: boolean;
};
type FeatureOptions = OptionsFlags<Features>;
const options: FeatureOptions = {
darkMode: true,
newProfile: false,
};
// Deliberately invalid: a flag must be boolean.
// const wrongOptions: FeatureOptions = {
// darkMode: () => {},
// newProfile: false,
// };
Here [K in keyof T] retains every source key, but boolean replaces each source property’s value type. FeatureOptions therefore has darkMode: boolean and newProfile: boolean, not functions. This pattern is worth using when every source feature is meant to have a corresponding flag. If only a few features should have flags, mapping the entire interface would describe the wrong contract.
The practical test is whether the two shapes are meant to move together. Derive a type when selected fields must continue to use the source’s property types, or when each source key deliberately produces an option. Write an explicit type when the resemblance is incidental or the new contract has different meaning; another layer of generics can make the promised shape harder to see. These examples establish static type compatibility for the shown declarations.

