This question is a follow up to this fantastic answer:
https://stackoverflow.com/a/64753409/1068446
What I'm wondering is why does the indexing in the mapped type trick work?
To give an example:
type MyData = {
a: {
alpha: string;
};
b: {
beta: number;
}
}
type NotNarrowPayload<T extends MyData> = {
key: keyof T;
fn: (data: T[keyof T]) => void;
}
type NarrowDataPayload<T extends MyData> = {
[K in keyof T]: {
key: K;
fn: (data: T[K]) => void;
}
}[keyof T];
function acceptsMTVTFunction<T extends MyData>(baseData: T,fn: NotNarrowPayload<T>) {
}
function acceptsMTVTFunction2<T extends MyData>(baseData: T,fn: NarrowDataPayload<T>) {
}
acceptsMTVTFunction(
{
a: {
alpha: "a"
},
b: {
beta: 99
},
},
{
key: "a",
fn: (data) => {
// TypeScript doesn't match the key to the value type, and this makes sense.
// (parameter) data: {
// alpha: string;
// } | {
// beta: number;
// }
}
}
);
acceptsMTVTFunction2(
{
a: {
alpha: "a"
},
b: {
beta: 99
},
},
{
key: "a",
fn: (data) => {
// (parameter) data: {
// alpha: string;
// }
}
}
);
Now, to be clear - I understand by the first approach doesn't work.
But what I'm not understanding is why the second approach does work.
If we take a closer look at this:
type NarrowDataPayload<T extends MyData> = { // We're declaring an object
[K in keyof T]: { // For each key in T, it will have a property
key: K; // And these values will depend on T and K
fn: (data: T[K]) => void;
}
}[keyof T]; // But only keep the types accessible by keyof T.
So the thing is - how does TypeScript know in this case that the key type we have used here is "a" and not "b".