Was this page helpful?

Mapped Types

When you don’t want to repeat yourself, sometimes a type needs to be based on another type.

Mapped types build on the syntax for index signatures, which are used to declare the types of properties which have not been declared ahead of time:

ts

A mapped type is a generic type which uses a union ofPropertyKeys (frequently createdvia akeyof) to iterate through keys to create a type:

ts
typeOptionsFlags<Type> = {
[PropertyinkeyofType]:boolean;
};
Try

In this example,OptionsFlags will take all the properties from the typeType and change their values to be a boolean.

ts
typeFeatures = {
darkMode: ()=>void;
newUserProfile: ()=>void;
};
 
typeFeatureOptions =OptionsFlags<Features>;
type FeatureOptions = { darkMode: boolean; newUserProfile: boolean;}
Try

Mapping Modifiers

There are two additional modifiers which can be applied during mapping:readonly and? which affect mutability and optionality respectively.

You can remove or add these modifiers by prefixing with- or+. If you don’t add a prefix, then+ is assumed.

ts
// Removes 'readonly' attributes from a type's properties
typeCreateMutable<Type> = {
-readonly [PropertyinkeyofType]:Type[Property];
};
 
typeLockedAccount = {
readonlyid:string;
readonlyname:string;
};
 
typeUnlockedAccount =CreateMutable<LockedAccount>;
type UnlockedAccount = { id: string; name: string;}
Try
ts
// Removes 'optional' attributes from a type's properties
typeConcrete<Type> = {
[PropertyinkeyofType]-?:Type[Property];
};
 
typeMaybeUser = {
id:string;
name?:string;
age?:number;
};
 
typeUser =Concrete<MaybeUser>;
type User = { id: string; name: string; age: number;}
Try

Key Remapping viaas

In TypeScript 4.1 and onwards, you can re-map keys in mapped types with anas clause in a mapped type:

ts
typeMappedTypeWithNewProperties<Type> = {
[PropertiesinkeyofTypeasNewKeyType]:Type[Properties]
}

You can leverage features liketemplate literal types to create new property names from prior ones:

ts
typeGetters<Type> = {
[PropertyinkeyofTypeas`get${Capitalize<string&Property>}`]: ()=>Type[Property]
};
 
interfacePerson {
name:string;
age:number;
location:string;
}
 
typeLazyPerson =Getters<Person>;
type LazyPerson = { getName: () => string; getAge: () => number; getLocation: () => string;}
Try

You can filter out keys by producingnever via a conditional type:

ts
// Remove the 'kind' property
typeRemoveKindField<Type> = {
[PropertyinkeyofTypeasExclude<Property,"kind">]:Type[Property]
};
 
interfaceCircle {
kind:"circle";
radius:number;
}
 
typeKindlessCircle =RemoveKindField<Circle>;
type KindlessCircle = { radius: number;}
Try

You can map over arbitrary unions, not just unions ofstring | number | symbol, but unions of any type:

ts
typeEventConfig<Eventsextends {kind:string }> = {
[EinEventsasE["kind"]]: (event:E)=>void;
}
 
typeSquareEvent = {kind:"square",x:number,y:number };
typeCircleEvent = {kind:"circle",radius:number };
 
typeConfig =EventConfig<SquareEvent |CircleEvent>
type Config = { square: (event: SquareEvent) => void; circle: (event: CircleEvent) => void;}
Try

Further Exploration

Mapped types work well with other features in this type manipulation section, for example here isa mapped type using a conditional type which returns either atrue orfalse depending on whether an object has the propertypii set to the literaltrue:

ts
typeExtractPII<Type> = {
[PropertyinkeyofType]:Type[Property]extends {pii:true } ?true :false;
};
 
typeDBFields = {
id: {format:"incrementing" };
name: {type:string;pii:true };
};
 
typeObjectsNeedingGDPRDeletion =ExtractPII<DBFields>;
type ObjectsNeedingGDPRDeletion = { id: false; name: true;}
Try

The TypeScript docs are an open source project. Help us improve these pagesby sending a Pull Request

Contributors to this page:
OTOrta Therox  (7)
SFSergey Falinsky  (2)
LLuke  (1)
Wwebstrand  (1)
SGHSteven G. Harms  (1)
5+

Last updated: Dec 16, 2025