Skip to content

instanceof type guard fails with a static toString method #27276

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
chharvey opened this issue Sep 21, 2018 · 6 comments
Closed

instanceof type guard fails with a static toString method #27276

chharvey opened this issue Sep 21, 2018 · 6 comments
Labels
Bug A bug in TypeScript
Milestone

Comments

@chharvey
Copy link

chharvey commented Sep 21, 2018

TypeScript Version: 3.0.3
Options: --strict --noImplicitAny --noImplicitThis --noImplicitReturns --target="ES2017" --module="CommonJS"

Search Terms: instanceof type guard class "toString"

Code:

declare class Thing { static toString(thing: any): string; } // <!-- here’s the problem
declare class Person extends Thing { name: string; }
declare class Robot extends Thing { serial: number; }

function test(smart_thing: Person|Robot): number {
	if (smart_thing instanceof Robot) {
		return smart_thing.serial // typeof `smart_thing` is Person|Robot,
		                          // and getting `.serial` fails
	} else return 0
}

Expected behavior:

The type of smart_thing should be narrowed to Robot.

Actual behavior:

The type of smart_thing has not been narrowed from Person | Robot.

When the static method Thing.toString is renamed or removed, or its parameter is deleted, the problem goes away.

Playground Link:

TypeScript Playground

@ghost
Copy link

ghost commented Sep 21, 2018

What is the code in Person.class.ts and Employee.class.ts?

@chharvey

This comment was marked as outdated.

@ghost
Copy link

ghost commented Sep 22, 2018

That reduced example is working when I try it.

@chharvey
Copy link
Author

@Andy-MS Ok so I think I found part of the problem. It weirdly involves a static toString method that takes a parameter, that inherits into each member of the union. It has nothing to do with imports. I've updated my code in the OP and the Playground.

@chharvey chharvey changed the title instanceof type guard fails when modules are imported instanceof type guard fails with a static toString method Sep 22, 2018
@ghost ghost added the Bug A bug in TypeScript label Sep 22, 2018
@RyanCavanaugh RyanCavanaugh added this to the Future milestone Oct 10, 2018
@thw0rted
Copy link

Hey, any movement on this? I just hit the same issue and it took me quite a while to figure out. I had an separate fooToString method that I moved into Foo as a static toString and suddenly all my type guards broke.

I couldn't get the OP playground link to work for me, but you can paste this in to see what I mean:

class A {
    public foo: string;
}

class B implements A {
    public foo: string;
    public bar: number;

    static toString(b: B) { return b.bar; }
}

class C implements A {
    public foo: string;
    public baz: boolean;

    static toString2(c: C) { return c.baz; } 
}

let x: A;
if (x instanceof B) { console.log(x.bar); }  // .bar is an error because X is still typed as A, not B
if (x instanceof C) { console.log(x.baz); }  // .baz works fine

Note that it's not only an issue when the superclass has a static toString, it's also a problem when one of the subclasses (the one that you're trying to narrow to, if that makes sense) has it.

@jakebailey
Copy link
Member

I'm looking at old bugs for ones that appear to be fixed; all of the snippets in this issue appear to have been fixed from at least 3.3 and up (maybe even older).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript
Projects
None yet
Development

No branches or pull requests

4 participants