Skip to content

Type guard affects type of variable in surprising way #50916

@MichaelMitchell-at

Description

@MichaelMitchell-at

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.

Metadata

Metadata

Assignees

Labels

BugA bug in TypeScriptFix AvailableA PR has been opened for this issue

Type

No type
No fields configured for issues without a type.

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions