interface PPP {
1: number
2: string
4: boolean
}
type Map1<T extends any[]> = {[K in keyof T]: T[K] extends keyof PPP ? PPP[T[K]] : never}
type Map2<T extends any[], U extends keyof T> = {[K in U]: T[K] extends keyof PPP ? PPP[T[K]] : never}
type Y1 = Map1<[1, 2]> // type Y1 = [number, string]
type Y2 = Map2<[1, 2], keyof [1, 2]> // type Y2 is not same as Y1
// type Y2 = {
// [x: number]: string | number;
// 0: number;
// 1: string;
// length: string;
// toString: never;
// toLocaleString: never;
// pop: never;
// push: never;
// concat: never;
// join: never;
// reverse: never;
// shift: never;
// slice: never;
// sort: never;
// ... 20 more ...;
// flat: never;
// }
As I know about TS, I would think Map1 is equivalent to Map2, however Y1 is not same as Y2, so there must be something that I don't know.
Question: what is the reason that causes this difference.
Typescript runs the type checking at build time, not runtime. Consider the following:
const a: (1 | 2 | 4)[] = [1, 1, 1, 1, 1]
const b: (1 | 2 | 4)[] = [1, 2, 4]
Both a and b are valid (since they are arrays that contain items that are either 1, 2 or 4.
keyof T on typescript lists all the properties of the type T. For an array, you can also run array.toString() or array.push(). Hence, toString and push appears as valid times.
Typescript will never be able to list, at compile time, the items on the array as your type.
Indeed, there is a bug on typescript regarding the different behaviour between keyof T coming from type argument to the keyof T in the type value.
Bug link
You can, though, bypass this issue with an Exclude
Link for the playground
interface PPP {
1: number
2: string
4: boolean
}
type Map1 < T extends any[] > = {
[K in keyof T]: T[K] extends keyof PPP ? PPP[T[K]] : never
}
type Map2 < T extends any[], U extends keyof T > = {
[K in U]: T[K] extends keyof PPP ? PPP[T[K]] : never
}
type Map3 < T extends any[], U extends keyof T > = {
[K in Exclude < U, keyof any[] > ]: T[K] extends keyof PPP ? PPP[T[K]] : never
}
type Y1 = Map1 < [1, 2] > // type Y1 = [number, string]
type Y2 = Map2 < [1, 2], keyof[1, 2] > // type Y2 is not same as Y1
type Y3 = Map3 < [1, 2], keyof[1, 2] > // Same as Y3
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