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

instanceof type inference broken in an "if -> else if -> else if" chain #11965

Closed
stkomarov opened this issue Oct 31, 2016 · 3 comments · Fixed by #19671
Closed

instanceof type inference broken in an "if -> else if -> else if" chain #11965

stkomarov opened this issue Oct 31, 2016 · 3 comments · Fixed by #19671
Assignees
Labels
Fixed A PR has been merged for this issue

Comments

@stkomarov
Copy link

stkomarov commented Oct 31, 2016

TypeScript Version: "typescript@2.0.6"

// A *self-contained* demonstration of the problem follows...
class Action1 {
  public constructor(public propA: string) {}
}

class ActionX {

}

class Action2 {
  public constructor(public propB: string) {}
}

class FooBar {

  public handleAction(action: Object): void {
    if (action instanceof Action1) {
      console.log(action.propA); // works
    } else if (action instanceof ActionX) {
      // The presense of this empty clause is essential
    } else if (action instanceof Action2) {
      console.log(action.propB); // throws compile error: Error TS2339: Property 'propB' does not exist on type 'never'.
    }
  }
}

Expected behavior:
The code to compile without an error.
Actual behavior:
The code above throws a compile error:
Error TS2339: Property 'propB' does not exist on type 'never'.

A workaround for the issue is to use an explicit cast for the last "else if" clause:

    } else if (action instanceof Action2) {
      const a = action as Action2;
      console.log(a.propB); // now it works
    }
@stkomarov stkomarov changed the title Type inference through instanceof broken in an "if -> else if -> else if" chain instanceof type inference broken in an "if -> else if -> else if" chain Oct 31, 2016
@aluanhaddad
Copy link
Contributor

aluanhaddad commented Oct 31, 2016

This is correct, expected behavior. TypeScript has a structural type system and all type tests, even instanceof are structural. Since instances of ActionX are structurally equivalent to {}, the type with no members, and since all types are assignable to {}, the type checker determines that the ensuing else branch is unreachable.

@stkomarov
Copy link
Author

stkomarov commented Nov 1, 2016

This is correct, expected behavior. TypeScript has a structural type system and all type tests, even instanceof are structural. Since instances of ActionX are structurally equivalent to {}, the type with no members, and since all types are assignable to {}, the type checker determines that the ensuing else branch is unreachable.

The last else if clause is reachable, and the code worked just fine with TS 1.8.10.

As a side note, a workaround for the original issue is to use an explicit cast and the code will compile with typescript 2.0.6:

  if (action instanceof Action1) {
      console.log(action.propA); // works
    } else if (action instanceof ActionX) {
      // The presense of this empty clause is essential
    } else if (action instanceof Action2) {
      const a = action as Action2;
      console.log(a.propB); // now it works
    }

@aluanhaddad
Copy link
Contributor

aluanhaddad commented Nov 1, 2016

It is not reachable from the standpoint of structural assignability. Even though it is technically reachable at runtime, because instanceof at runtime compares two references, instanceof at compile time compares structurally. The following simplified example illustrates the behavior.

class ActionX {}
class FooBar {
    public handleAction(action: Object): void {
        if (action instanceof ActionX) {
            return;
        }
        action // has type never
    }
}

@RyanCavanaugh RyanCavanaugh added the Needs Investigation This issue needs a team member to investigate its status. label May 24, 2017
@mhegazy mhegazy added Fixed A PR has been merged for this issue and removed Needs Investigation This issue needs a team member to investigate its status. labels Nov 2, 2017
@mhegazy mhegazy added this to the TypeScript 2.7 milestone Nov 2, 2017
@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
Fixed A PR has been merged for this issue
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants