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

Union types of sort interface | interface[] and using instanceof Array #2295

Closed
IanYates opened this issue Mar 11, 2015 · 5 comments
Closed
Labels
Bug A bug in TypeScript Duplicate An existing issue was already created

Comments

@IanYates
Copy link

In TypeScript playground, the "New Features" option gives this code

type NameOrNameArray = string | string[];

function createName(name: NameOrNameArray) {
    if (typeof name === "string") {
        return name;
    }
    else {
        return name.join(" ");
    }
}

var greetingMessage = `Greetings, ${createName(["Sam", "Smith"]) }`;
alert(greetingMessage);

If I make a type such as

interface Guid {
  toLowerCase:()=>string;
}

(since I'm using TypeLite to get some C# types in my TypeScript I have a Guid type but basically treat Guids as strings so toLowerCase() is enough to discriminate for my purposes).

Anyway, if I then modify the playground code to be

interface Guid {
  toLowerCase:()=>string;
}
type GuidOrGuidArray = Guid | Guid[];

function createGuid(g: GuidOrGuidArray) {
    if (g instanceof Array) {
      //intellisense on g.  should give me array things
      var x = g[0];     //This SHOULD compile
   }
    else {
        //intellisense on g.  should give me toLowerCase() as my only option
        var y = g.toLowerCase();   ///This SHOULD compile
    }
}

Instead I get the type of x to be any and y fails to compile. In both cases, the popup hint in the playground (and in Visual Studio) gives the indication that the type of g within each branch of the if is still the union type.

You'll note that I switched the if block around so that I'm using instanceof Array rather than typeof === "string". That's because I've tried it the other way and wasn't surprised that it didn't work since my Guid interface doesn't extend string in any way and typeof [] gives object.

Should this work? At the moment I'm doing the kludge workarounds...
var x = <Guid[]>g[0];
and
var y = (<Guid>g).toLowerCase();

It'd be 💯 if instanceof Array worked to discriminate between the array and non-array in the union type.

@NN---
Copy link

NN--- commented Mar 11, 2015

I guess it is: #1657 #1708 and #1007 .

@ahejlsberg
Copy link
Member

Yes, this issue was fixed in #1657.

@ahejlsberg ahejlsberg added Duplicate An existing issue was already created Bug A bug in TypeScript labels Mar 11, 2015
@IanYates
Copy link
Author

Thanks for the quick response! I had read 1657 in the past but didn't realise it wasn't in the 1.4 release. Apologies for the duplicate.

@IanYates
Copy link
Author

After updating to the latest code from master (replacing my language service and compiler files used by VS 2013), the inference still isn't 100% correct. The "g" in the else block is still being seen as the union type rather than the non-array "Guid" type.

capture

The example given by @ahejlsberg in the PR

class Message {
    value: string;
}

function saySize(message: Message | Message[]) {
    if (message instanceof Array) {
        return message.length;  // message of type Message[] here
    }
}

doesn't mention the else clause - am I just looking for something that isn't (won't?) be supported. No problem if that's the case - just want to be sure.

I can see slightly differing issues & discussions about this
#1957 - this seems to be along the same lines as what I'm looking for. It references
#1719 - @ahejlsberg mentions that #1007 is the ultimate solution for this.
#1785 - similar
#1587 - similar

I'll keep doing my typecasts for the time being and hope for #1007 to come along or even see the #1719 suggestion to narrow types in else clauses of instanceof checks implemented.

@ahejlsberg
Copy link
Member

It is by design that the type guard doesn't affect the else block. As described in #1719 the reasoning is that while we know the instance wasn't created by the Array constructor function, it might still implement the Array interface (through structural typing).

@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
Projects
None yet
Development

No branches or pull requests

3 participants