Skip to content

Exhaustive type checking not working when class field to match on is a union #40160

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
francium opened this issue Aug 21, 2020 · 2 comments
Closed
Labels
Duplicate An existing issue was already created

Comments

@francium
Copy link

francium commented Aug 21, 2020

TypeScript Version: Nightly (v4.1.0-dev.20200807)

Search Terms: Tagged union, discriminated union, exhaustive checking

Code
TypeScript Playground Example

class Either<T> {
  public static Left<T>(value: T): Either<T> {
    return new Either('left', value);
  }

  public static Right<T>(value: T): Either<T> {
    return new Either('right', value);
  }

  private constructor(
    public readonly kind: 'left' | 'right',
    public readonly value: T
  ) {}
}

function doSomething(): string {
  const e = Either.Left('foo');

  // e.kind: 'left' | 'right';
  switch (e.kind) {
    case 'left':
      return e.value;
    case 'right':
      return e.value;
    // case 'foo':      as expected, this doesn't work
    //   break;
    default:
      const _exhaustiveCheck: never = e;        // even though we've matched against all the possible cases of `kind`, compiler still complains here
      throw new Error("How did we get here?");
  }

}

However this can be made to work if we did this,

interface Left {
  kind: 'left';
  value: string;
}
interface Right {
  kind: 'right';
  value: string;
}
type Either = Left | Right;

Playground
Now doing switch (e.kind) would perform the right checks.

Expected behavior:
Exhaustive type checking should work in the case with the class Either and kind: 'left' | 'right'.

Actual behavior:
Exhaustive type checking fails even though compiler understand the union only has 2 fields (e.g. case 'foo' is an error).

@jack-williams
Copy link
Collaborator

Duplicate of #38963. The narrowing of a property is not propagated back to the parent object unless the parent is a discriminated union type.

You can use the property as evidence of unreachability:

const _exhaustiveCheck: never = e.kind;
throw new Error("How did we get here?");

In theory being able to conjure any value of type never is sufficient to show that the statement is unreachable.

@RyanCavanaugh RyanCavanaugh added the Duplicate An existing issue was already created label Aug 21, 2020
@typescript-bot
Copy link
Collaborator

This issue has been marked as a 'Duplicate' and has seen no recent activity. It has been automatically closed for house-keeping purposes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

4 participants