Description
Bug Report
As of TypeScript 4.3, when inlining a mapped type on the left-hand side of extends
in a conditional type, TypeScript incorrectly evaluates the condition when the right-hand side contains another mapped type with infer
and renders the false condition. When declaring the mapped type explicitly rather than inline, the ternary is correctly evaluated.
This is pretty hard to cover in words, so please check playground link for reproduction.
The language version can be changed to 4.2 to see it working correctly.
🔎 Search Terms
typescript infer inline mapped type never generic inference
🕗 Version & Regression Information
- This changed between versions 4.2 and 4.3
⏯ Playground Link
Change TS language to 4.2 to see the code functioning correctly, and 4.3 or 4.4 to see failure.
Playground link with relevant code
💻 Code
type DefinedKeys<T> = {
[K in keyof T]:
string extends K ? never :
number extends K ? never :
K }
type KnownKeys<T> = DefinedKeys<T> extends {
[_ in keyof T]: infer U
}
? U
: never;
// This type is the exact same as `KnownKeys`, but inlines `DefinedKeys<T>` instead of declaring it as a separate type
type FailingKnownKeys<T> = {
[K in keyof T]:
string extends K ? never :
number extends K ? never :
K } extends {
[_ in keyof T]: infer U
}
? U
: never;
interface Flags {
knownFlag1: boolean
knownFlag2: boolean
[x: string]: boolean | null | undefined;
}
type DefinedValidationKeys = DefinedKeys<Flags>
type KnownFlags = KnownKeys<Flags>
type FailingKnownFlags = FailingKnownKeys<Flags>
🙁 Actual behavior
In the above code sample, the type FailingKnownFlags
is never
(the false branch of the ternary of FailingKnownKeys<T>
), while KnownFlags
correctly is {knownFlag1: boolean, knownFlag2: boolean}
🙂 Expected behavior
KnownKeys<T> === FailingKnownKeys<T>
They are the same declaration, just one being inline and the other with an explicitly extracted type.