See the following code:
interface X {
a: string;
b: number;
c: boolean;
}
type A = {
prop: keyof X;
value: X[keyof X];
}
const a: A = { prop: 'a', value: 'a' }; // ok
const b: A = { prop: 'a', value: 1 }; // not ok, value should be string, because X['a'] is string
const c: A = { prop: 'b', value: 1 }; // ok
const d: A = { prop: 'c', value: 1 }; // not ok, value should be boolean, because X['c'] is boolean
Here I want the type of the .value property to be string, if prop is "a", number for "b" and boolean for "c", but instead it is string|number|boolean for all cases, because keyof X can refer to different keys for each use for type A. How can I make it refer to the same property twice, without having it explicitly input it into a generic argument of A?
I feel like I should be using infer her, but I'm not sure how, and also I might be on the wrong track there.
You want A to be a union of {prop: K; value: X[K]} for each K in keyof X, like this:
type A = {
prop: "a";
value: string;
} | {
prop: "b";
value: number;
} | {
prop: "c";
value: boolean;
};
In each element of that union, there is a correlation between the prop type and the value type, which prohibits you from assigning prop and value types from different members of X:
const a: A = { prop: 'a', value: 'a' }; // ok
const b: A = { prop: 'a', value: 1 }; // error
const c: A = { prop: 'b', value: 1 }; // ok
const d: A = { prop: 'c', value: 1 }; // error
You can also make the compiler calculate this for you programmatically in a few ways, such as building a mapped type that you immediately index into:
type A = { [K in keyof X]-?: {
prop: K;
value: X[K];
} }[keyof X];
It can be verified via IntelliSense that the above definitions of A are equivalent, but now A will update automatically if you modify X.
Playground link to code
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With