TypeScript keyof Operator usage in Mapped types
Mapped types in TypeScript allow you to create new types by transforming or modifying the properties of an existing type. It can be achieved in several ways.
One of them is by using the keyof
operator.
The textbook definition for this operator is: “The keyof
operator takes an object type and produces a string or numeric literal union of its keys.”
type PowerType = 'EV' | 'ICE';
type Car = {
brand: string;
model: string;
year: number;
type: PowerType
};
type carKeys = keyof Car; // "brand" | "model" | "year"| "type"
const car: Car = {
brand: 'Tesla',
model: 'Model 3',
year: 2024,
type: 'EV'
};
Perhaps, you want a particular key to allow having undefined
:
type OptionalCar = {
[K in keyof Car]: K extends 'year' ? Car[K] | undefined : Car[K];
};
const carOfUnknownYear: OptionalCar = {
brand: 'Tesla',
model: 'Model 3',
year: undefined,
type: 'EV'
};
OR you want to produce a cutdown version according to your need, let’s say, removing certain fields:
type BasicCar = Pick<Car, Exclude<keyof Car, 'year' | 'type'>>;
const basicCar: BasicCar = {
brand: "Tesla",
model: "Model 3"
};
OR you want to make everything optional:
type OptionalCar = {
[K in keyof Car]?: Car[K];
};
const minimalCar: OptionalCar = {
brand: 'Tesla'
};
[K in keyof Car]
: This is the syntax for a mapped type. It iterates over each property (K
) in the set of keys of the Car
type (keyof Car
). It essentially says "for each property in Car
.
?
: This is the optional property modifier. It indicates that each property in the resulting type is optional.
Car[K]
: This represents the type of the property K
in the original Car
type. It's using an indexed access type (Car[K]
) to get the type of the property with the key K
.
Hence, it’s exactly like:
type OptionalCar = {
brand?: string;
model?: string;
year?: number;
type?: PowerType;
};
Disclaimer: some of the above examples can be replaced with TypeScript utility types.
For instance, there’s a more elegant approach to achieve making all the keys optional:
type OptionalCar = Partial<Car>;
You can find out more about the Mapped types here