Skip to content
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

Exhaustive switch on a type union with a default case yields TS2339: Property 'x' does not exist on type 'never' #9838

Closed
Gilead opened this issue Jul 20, 2016 · 7 comments
Assignees
Labels
Bug A bug in TypeScript Fixed A PR has been merged for this issue

Comments

@Gilead
Copy link

Gilead commented Jul 20, 2016

TypeScript Version: 2.0.0

Code

type TestType = 'foo' | 'bar';

interface TestInterface {
  type : TestType;
}

class Test {
  test = (param : TestInterface) : boolean => {
    switch (param.type) {
      case 'foo' : return true;
      case 'bar' : return false;
      default    : throw Error("Invalid type: "+ param.type); // TS2339: Property 'type' does not exist on type 'never'.
    }
  }
}

Expected behavior:
The code compiles with no errors.

Actual behavior:
tsc test.ts
test.ts(14,53): error TS2339: Property 'type' does not exist on type 'never'.

I understand the compiler knows that with the current code the default case can never be reached and the type property can not have any possible valid TestType value in this case but the whole point of having a default case is to limit the scope of the human error, compiling with a different TS version, compiler flags etc. It can also be considered a safe coding practice.

The workaround is easy but ugly and should not be necessary:

default    : throw Error("Invalid type: "+ (<any>param).type);
@HerringtonDarkholme
Copy link
Contributor

The code above cannot go wrong in TypeScript land, as you have noted. The only reasonable error I can conceive is someone pass a value from the outside of TS or someone naughtily assert any.

In any case, I think your workaround is a good alarm of someone do wrong thing somewhere, though.

@Gilead
Copy link
Author

Gilead commented Jul 20, 2016

Indeed. Another example is a function called from a HTML template event handler with no type safety in which case you can just have a typo somewhere trigger the default case. This is why I believe TypeScript should allow seemingly unnecessary default cases.

@yahiko00
Copy link

I dislike this current behavior. This should not be an error. It is quite weird TypeScript could change the type of an object (to never or anything else).

@yortus
Copy link
Contributor

yortus commented Jul 21, 2016

If you can't trust that the passed in param is really a TestInterface at runtime, then you probably shouldn't assume it's an object with a type property. If for example param is actually null or undefined, then the code above will throw a TypeError trying to access the type property. If it's a string or number, then it will throw "Invalid type: ".

@RyanCavanaugh
Copy link
Member

Seems wrong -- param should be of type TestInterface and param.type should be of type never

@RyanCavanaugh RyanCavanaugh added the Bug A bug in TypeScript label Jul 21, 2016
@mhegazy mhegazy added this to the TypeScript 2.0.1 milestone Jul 21, 2016
@mhegazy mhegazy assigned ahejlsberg and unassigned sandersn Jul 22, 2016
@RyanCavanaugh
Copy link
Member

Seems to be fixed in Anders' current literal types PR

@ahejlsberg
Copy link
Member

Fixed in #9407.

@ahejlsberg ahejlsberg added the Fixed A PR has been merged for this issue label Aug 1, 2016
@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Bug A bug in TypeScript Fixed A PR has been merged for this issue
Projects
None yet
Development

No branches or pull requests

8 participants