Skip to content

Type guard fails to narrow union type #1802

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
altano opened this issue Jan 25, 2015 · 6 comments
Closed

Type guard fails to narrow union type #1802

altano opened this issue Jan 25, 2015 · 6 comments
Labels
Bug A bug in TypeScript Duplicate An existing issue was already created Fixed A PR has been merged for this issue

Comments

@altano
Copy link

altano commented Jan 25, 2015

This works, AS EXPECTED:

class Cat {}
class Dog {}

function logType1(animal: Cat|Dog) {
    if (animal instanceof Cat) {
        console.log("cat");
    }
    else if (animal instanceof Dog) {
        console.log("dog");
    }
}

This doesn't work, which is debatable since undefined instanceof Cat is legal JS but I prefer the type warning since it catches errors:

function logType2(animal: Cat|Dog|void) {
    if (animal instanceof Cat) {
        console.log("cat");
    }
    else if (animal instanceof Dog) {
        console.log("dog");
    }
}

This doesn't work, but I would definitely expect it to:

function logType3(animal: Cat|Dog|void) {
    if (typeof animal === "undefined" || animal === null) { // edited to check for null as well, since void can be either
        console.log("no animal");
    }
    else if (animal instanceof Cat) {
        console.log("cat");
    }
    else if (animal instanceof Dog) {
        console.log("dog");
    }
}

See playground for code.

As it stands, if a union type contains void, I don't know how to use instanceof with it.

@altano
Copy link
Author

altano commented Jan 25, 2015

Worth noting that this compiles fine, which is inconsistent:

function logType4(animal?: Cat) {
    if (animal instanceof Cat) {
        console.log("Cat");
    }
}

Since void is null or undefined, I think this behavior is inconsistent.

@danquirk
Copy link
Member

What exactly are you trying to accomplish by putting void in the union parts?

@altano
Copy link
Author

altano commented Jan 27, 2015

@danquirk in my code there's a callback function that I am not in control of, which can return a value of one of 2 types or nothing (undefined). I'm trying to determine what type was returned by the function and I'm hitting this issue as a result. It looks more like this:

interface AnimalOrNothing {
    (): Dog | Cat | void;
}

var getAnimal: AnimalOrNothing = () => {
    if (Math.random() > 0.5) {
        return new Dog();
    }
    else if (Math.random() > 0.5) {
        return new Cat();
    }
    else {
        return;
    }
}

var unknown = getAnimal();
if (unknown instanceof Dog) {
    unknown.bark();
}
else if (unknown instanceof Cat) {
    unknown.pur();
}
else {

}

Note that I don't have control over the method that can either return a value or nothing.

@NoelAbrahams
Copy link

Since void is a basic type this issue looks to be related to #1785. The problem is when primitives are unioned with object types the compiler is not able to resolve instanceof.

@ahejlsberg
Copy link
Member

With the fix in #1803, the instanceof operator now allows unions that contain primtives (including void), so I'm closing this bug.

@ahejlsberg ahejlsberg added Bug A bug in TypeScript Duplicate An existing issue was already created Fixed A PR has been merged for this issue labels Jan 28, 2015
@jbondc
Copy link
Contributor

jbondc commented Feb 3, 2015

Isn't 'void' implicit in any type?

interface AnimalOrNothing {
    (): string | number;
}

var getAnimal: AnimalOrNothing = () => {
    if (Math.random() > 0.5) {
        return new Dog();
    }
    else if (Math.random() > 0.5) {
        return new Cat();
    }
    else {
        return; // allowed already!
    }
}

var unknown = getAnimal();
if (unknown instanceof Dog) {
    unknown.bark();
}
else if (unknown instanceof Cat) {
    unknown.pur();
}
else {
  unknown // should narrow to void
}
// Or alternative 
if (unknown == null) {
    unknown // narrows to void
}
else if (unknown instanceof Cat) {
    unknown.pur();
}
else {
    unknown.bark();
}

@microsoft microsoft locked and limited conversation to collaborators Jun 18, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Bug A bug in TypeScript Duplicate An existing issue was already created Fixed A PR has been merged for this issue
Projects
None yet
Development

No branches or pull requests

5 participants