-
Notifications
You must be signed in to change notification settings - Fork 13k
Closed
Labels
BugA bug in TypeScriptA bug in TypeScriptFixedA PR has been merged for this issueA PR has been merged for this issue
Milestone
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
gcnew
Metadata
Metadata
Assignees
Labels
BugA bug in TypeScriptA bug in TypeScriptFixedA PR has been merged for this issueA PR has been merged for this issue