Closed
Description
Minimal repro:
type AB = {
a: 'a'
b: 'a'
}
type T1<K extends keyof AB> = { [key in AB[K]]: true }
type T2<K extends keyof AB> = T1<K>[K] // BUG: should be an error for K = 'b'
I came across this in production code and made a contrived example to show the issue. Might be related to #15756.
Note that there might be two bugs here in play, one with mapped types and one with lookup types on mapped types. Once I start pushing the types a little, I come across a wild range of unexpected behavior.
Pay attention to the last type 'G', where the compiler is suddenly correct again, which is probably the oddest one of all.
// Let's make a simple type
type StringA = "a"
type A = {
a: StringA
b: StringA
}
// so type A[S] should always resolve to "a" for any valid keys
// note that it doesn't collapse T, even though it could
type T<S extends "a" | "b"> = A[S] // expands A, but doesn't collapse the indexer
let t1: T<"a"> // t1: "a"
let t2: T<"b"> // t1: "a"
// Let's make a silly generic type that only keeps key "a"
// The compiler correctly infers that this new type is just { a: true }
type B<S extends "a" | "b"> = {[key in "a"]: true} // B = { a: true }
// Let's make the type more generic, remember that A[S] is actually just "a"
// The compiler now infers that the type of C is {}, this is plain wrong
type C<S extends "a" | "b"> = {[key in A[S]]: true} // C = {}
// Obviously, we couldn't do a lookup on such a type
// Compiler correctly complains that we can't index an empty object
type D<S extends "a" | "b"> = {}[S] // Type 'S' cannot be used to index type '{}'.
// Now let's add a lookup to the generic version
// Curiously, it doesn't fail this time, but infers 'true', how odd
type E<S extends "a" | "b"> = {[key in A[S]]: true}[S] // = true
// It even happens when we index the above type C, which the compiler claims is {}
type F<S extends "a" | "b"> = C<S>[S] // = true
// Let's also try to use this type, it will become important later
// This does exactly what's expected
let a1: F<"a"> // a: true
let b1: F<"b"> // b: true
// Now let's make it even weirder and add a general indexer
// This correctly doesn't fail as any key is now valid
// The type it infers though, is just plain weird:
// G = ({} & { [key: string]: false; })[S]
type G<S extends "a" | "b"> = (
C<S> &
{[key: string]: false}
)[S]
// And now the weirdest thing of all, let's use this type
// It correctly infers the result! Both of these are correct!
let a2: G<"a"> // a: true
let b2: G<"b"> // b: false
This has to be the weirdest bug I've run across