This write up is based on Matt Pocock’s TypeScript tip thread.
Problem
Say that we have some object (with some properties, of course), say:
const fruitCounts = {
apple: 1,
pear: 2,
};
And we’d like another type that represents individual fruit counts, i.e we want to end up with the following type:
type SingleFruitCount =
| {
apple: number;
}
| {
banana: number;
};
// in use
const singleFruitCount: SingleFruitCount = {
apple: 1,
};
This works, but typing it manually this way is clumsy. We can imagine how hairy the type would get with a large number of properties. Ideally, we’d be able to derive the type from the fruitCounts
object itself.
Solution
Here’s the derivation:
// store type so that its keys can be used
type FuritCounts = typeof fruitCounts;
// our derivation
type SingleFruitCount = {
[K in keyof FruitCounts]: {
[K2 in K]: number;
};
}[keyof FruitCounts];
// in use
const singleFruitCount: SingleFruitCount = {
apple: 1,
};
Breakdown of the derivation:
We use mapped types to create a type that we can use in the next step:
type SingleFruitCount = {
// using a mapped type to iterate over fruitCount keys
[K in keyof FruitCounts]: {
// using a mapped type (again) over fruitCount keys
[K2 in K]: number;
};
};
// produces the following type
type SingleFruitCount = {
apple: {
apple: number;
};
pear: {
pear: number;
};
};
That is not useful in and of itself. To create the desired union out of the type above, we can leverage indexed access types:
type SingleFruitCount = {
[K in keyof FruitCounts]: {
[K2 in K]: number
}[keyof FruitCounts] // form a union and use the properties for index access
// produces the desired union type
type SingleFruitCount= {
apple: number;
} | {
pear: number;
}
To further clarify: in the same way that we would retrieve an object property value by passing its key with the square bracket notation (e.g. fruitObject[apple]
), we can pick out a type from another type by passing the property key of the sought out type to the original type within square brackets.
We need a union, and the way that keyof
works is that for any type T, keyof T
produces a union of property names of T. In our case, keyof FruitCounts
creates a union of apple | pear
since those are the property names of FruitCounts
.
Once the property name union has been formed by keyof
, the types that are mapped to those property names are returned and we end up with the final result.