Imagine having a type named Foo
type Foo = {
a: string;
b: number;
c: boolean;
}
Now, I am looking to create a type for an object containing a key
and a value
of a designated type T
. The goal is for the value's type to be automatically determined based on the key, allowing me to do this:
const kv1: KeyValue<Foo> = {key: 'a', value: 'STRING'};
const kv2: KeyValue<Foo> = {key: 'b', value: 42};
const kv3: KeyValue<Foo> = {key: 'c', value: true};
If I use the following definition:
type KeyValue<T> = {
key: keyof T;
value: T[keyof T]
}
... then the values will encompass all properties' values in Foo
:
And if I define it like this:
type KeyValue<T, K extends keyof T> = {
key: K;
value: T[K]
}
...then while explicitly specifying as KeyValue<Foo, 'a'>
allows creating objects that match Foo
's property types, using just KeyValue<Foo, keyof Foo>
permits each value to take on any type from string | number | boolean
, not inferred from the entered key
.
The ultimate aim is to achieve scenarios like these:
const kvs: Array<KeyValue<Foo>> = [
{key: 'a', value: 'STRING'}, // should reject if value is not a string
{key: 'b', value: 42}, // should reject if value is not a number
{key: 'c', value: true}, // should reject if value is not a boolean
];
Is there a way to establish the KeyValue
type with these limitations, effectively evolving into
KeyValue<Foo, 'a'> | KeyValue<Foo, 'b'> | KeyValue<Foo, 'c'>
without manually listing this union? Essentially, the desire is to infer the value
type based on the given key
in the current object literal.