Mapped type is one of the type manipulations you can do with Typescript. It is useful when you want to derive a type from another type but have the properties of the new type go through some manipulation.
The concept at its core is pretty similar to Array.map()
method in Javascript. If you want to derive a new array from an existing array, rather than repeating the new array, you can make use of the Array.map()
method and iterate through the old array to generate a similar/different array.
This is basically what we are trying to do with mapped types.
What is a Mapped Type
A mapped type is a generic type which uses a union of PropertyKey
s (frequently created via a [keyof](https://www.typescriptlang.org/docs/handbook/2/indexed-access-types.html)
) to iterate through keys to create a type.
Let’s say you have a Type with functions as its properties and you want to generate a new type with the same properties but as booleans, you can do this:
type MappedToBoolean <Type> = {
[Property in keyof Type]: boolean
}
type Animal = {
fly: () => void;
run: () => void;
swim: () => void
}
type NewAnimal = MappedToBoolean<Animal>
const dog: NewAnimal = {
fly: false,
run: true,
swim: true
}
Doing this, below, would result in a type error:
const dog: NewAnimal = {
fly: () => undefined, // '() => undefined' is not assignable to type 'boolean'
run: true,
swim: true
}
What are 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 them with these -
(to add) or +
(to delete).
For instance, to map through a type and make all its properties optional, you can just add the +
modifier as such:
type MappedToBoolean <Type> = {
[Property in keyof Type]+?: Type[Property]
}
Mapping using the as
keyword
You can remap the type of the keys into other types as such:
type MappedTypeWithNewProperties<Type> = {
[Properties in keyof Type as NewKeyType]: Type[Properties]
}
Use Cases
Knowing how to manipulate different types can give you the control to efficiently create and manage modular or expansible types.
A simple scenario where I used this once was when I need a function that can take the sub-type of an object and search through an array of objects to see if a super object exists.
A sub-type of an object is literally one where all the properties can be optional so I created a new type of the existing type where all the properties are optional.
type MakePropertiesOptional<Type> = {
[Property in keyof Type]+?: Type[Property]
}
type Person = {
name: string;
age: number;
school: string;
state: string;
number: string;
country: string
}
function searchArrayOfPersons(person: MakePropertiesOptional<Person>) {
// .. some more codes
return undefined
}
searchArrayOfPersons({name: "Kayode"})
searchArrayOfPersons({address: "some address"})
// Type Error: '{ address: string}': is not assignable to parameter
// of 'MakePropertiesOptional<Person>`
If you enjoy reading this article, you can support my hobby by buying me a coffee.