Closed
Description
Bug Report
π Search Terms
type guard / fall through / narrow / change / lazy / evaluate
π Version & Regression Information
- This changed between versions v4.7.4 and v4.8.2
β― Playground Link
Playground link with relevant code
π» Code
type Identity<T> = {[K in keyof T]: T[K]};
type Self<T> = T extends unknown ? Identity<T> : never;
function is<T>(value: T): value is Self<T> {
return true;
}
type Union = {a: number} | {b: number} | {c: number};
function example(x: Union) {
if (is(x)) {}
if (is(x)) {}
if (is(x)) {}
if (is(x)) {}
if (is(x)) {}
if (is(x)) {}
if (is(x)) {}
if (is(x)) {}
return x;
// ^?
}
π Actual behavior
The type of x
is "narrowed" to Identity<Identity<Identity<Identity<Identity<Identity<Identity<Identity<{a: number;}>>>>>>>> | Identity<Identity<Identity<Identity<Identity<Identity<Identity<Identity<{b: number;}>>>>>>>> | Identity<Identity<Identity<Identity<Identity<Identity<Identity<Identity<{c: number;}>>>>>>>>
It's as if a variable gets narrowed to the union of the types of both sides of the type predicate, e.g.
if (isA(aOrB)) {
// `aOrB` gets narrowed to `A`
} else {
// `aOrB` gets narrowed to `Exclude<typeof aOrB, A>`
}
// `aOrB` gets narrowed to `A | Exclude<typeof aOrB, A>` but it should just be left alone
π Expected behavior
The type of x
doesn't change.