Reputation: 3286
How do I infer all values from an object like this…
const fruits = {
a: 'apple',
b: 'banana'
};
…so that I get this?
type fruitValues = 'apple' | 'banana';
(I believe I'm looking for the equivalent of $Values<T>
in Flow)
Upvotes: 3
Views: 118
Reputation: 327994
You can infer the type from the object, but the object has already been inferred to hold only string
properties. That is, the types "apple"
and "banana"
have been widened to string
, as mentioned in the relevant GitHub issue, because they are non-readonly
properties.
const fruits = {
a: 'apple',
b: 'banana'
};
type FruitValues = (typeof fruits)[keyof typeof fruits]; // string
So the above FruitValues
is not what you want. If you want to get closer, you need to prevent that widening. One way is to use redundant but self-contained type assertions:
const fruits = {
a: 'apple' as 'apple',
b: 'banana' as 'banana'
};
type FruitValues = (typeof fruits)[keyof typeof fruits]; // "apple" | "banana"
Another way is to make a helper function that infers narrower types:
// put in a library somewhere
type Narrowable = string | number | boolean | undefined | null | void | {};
const narrowObj = <V extends Narrowable, T extends { [k: string]: V }>(t: T) => t;
const fruits = narrowObj({
a: 'apple',
b: 'banana'
})
type FruitValues = (typeof fruits)[keyof typeof fruits]; // "apple" | "banana"
Finally, once TypeScript 3.4 lands there will be const
contexts which will infer the narrowest possible type (including making all the properties readonly
which you might not want), like this:
const fruits = {
a: 'apple',
b: 'banana'
} as const; // requires TS3.4+
type FruitValues = (typeof fruits)[keyof typeof fruits]; // "apple" | "banana"
Okay, hope that helps. Good luck!
Upvotes: 7