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