Skip to content

Widen union type discriminant to all compile time constants #11656

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
akarzazi opened this issue Oct 15, 2016 · 10 comments
Closed

Widen union type discriminant to all compile time constants #11656

akarzazi opened this issue Oct 15, 2016 · 10 comments
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed Out of Scope This idea sits outside of the TypeScript language design constraints Suggestion An idea for TypeScript

Comments

@akarzazi
Copy link

akarzazi commented Oct 15, 2016

TypeScript Version: 2.0.3 / nightly (2.1.0-dev.201xxxxx)

This is a suggestion

Code

interface Calculator1Result {
    value1: number,
    calculationType: typeof Calculator1
}

interface Calculator2Result {
    value1: number,
    value2: number,
    calculationType: typeof Calculator2
}

class Calculator1 {
    doWork(): Calculator1Result {
        return {};
    }
}
class Calculator2 {
    doWork(): Calculator2Result {
        return {};
    }
}

function analyse(a: Calculator1Result | Calculator2Result) {
    if (a.calculationType === Calculator2) {
        a. // is still Calculator1Result | Calculator2Result
        // it should be Calculator2Result
    }
}

Expected behavior:
Widen union discriminator to all compile time constants
Actual behavior:
Union discriminator can only be of type String Literal

@aluanhaddad
Copy link
Contributor

aluanhaddad commented Oct 15, 2016

I'm pretty sure that typeof Calculator2 is not a compile time constant.

@akarzazi akarzazi changed the title Widen union discriminator to all compile time constants Widen union type discriminant to all compile time constants Oct 15, 2016
@akarzazi
Copy link
Author

Why types would not be a compile time constants in a statically typed language ?

@mhegazy
Copy link
Contributor

mhegazy commented Oct 18, 2016

TypeScript type system is structural. so typeof Calculator is not a "constant", it is a "shape".

@mhegazy mhegazy added Out of Scope This idea sits outside of the TypeScript language design constraints Design Limitation Constraints of the existing architecture prevent this from being fixed Suggestion An idea for TypeScript labels Oct 18, 2016
@akarzazi
Copy link
Author

Whether typeof Calculator is a "shape" or a "constant", it is known at compile time.
Then it should be possible to use it as discriminant.
As you can see in the sample below, type narrowing works already fine on "shapes". (see testType method)

interface Calculator1Result {
    value1: number,
    calculationType: typeof Calculator1
}

interface Calculator2Result {
    value1: number,
    value2: number,
    calculationType: typeof Calculator2
}

class Calculator1 {
    private _: any;
    doWork(): Calculator1Result {
        return  null as Calculator1Result;
    }
}
class Calculator2 {
    private _: any;
    doWork(): Calculator2Result {
         return  null as Calculator2Result;
    }
}

function testType(a: typeof Calculator1 |typeof Calculator2) {
    if (a === Calculator2) {
        a. // is typeof Calculator2 , good ! 
       // Type narrowing works fine !  
    }
}

function analyse(a: Calculator1Result | Calculator2Result) {
    if (a.calculationType === Calculator2) {
        a. // is still Calculator1Result | Calculator2Result
        // it should be Calculator2Result
        // Type discrimination is ko !  
    }
}

@mhegazy
Copy link
Contributor

mhegazy commented Oct 19, 2016

Whether typeof Calculator is a "shape" or a "constant", it is known at compile time.

No, it is not. consider:

class Calculator1 {
    doWork(): Calculator1Result {
        return {};
    }
}

var c: Calculator1 = new Calculator1();

console.log(c instanceof Calculator1); // true

c = {
    doWork() { }
};

console.log(c instanceof Calculator1); // false

The type system is structural. i would recommend reading https://github.com/Microsoft/TypeScript/wiki/FAQ#what-is-structural-typing.

@akarzazi
Copy link
Author

Since I've read nearly all the wiki, I'm pretty much aware of the structural typing consequences.
That's why I added private _: any; member on the last code sample.

The question is:
So why would the type narrowing work, but not the discrimination when comparing on types ? ( as you can see in my last sample)

Beside this, from the Design Meeting Notes, 5/6/2016 #8503
It seems that some work will be done around classes and instanceof type guard
And as you said in #7271 : As noted in #8503, classes should be treated definitely for instanceof checks.

This is good news and may affect class type comparison somehow.

@aluanhaddad
Copy link
Contributor

aluanhaddad commented Oct 26, 2016

@akarzazi it is worth noting that compile time constants are generally primitive. So a class is not a compile time constant in the sense that its value can be mutated.

@akarzazi
Copy link
Author

@aluanhaddad I don't think that the member of type typeof Calculator1 is mutable.
The exposed type is interface Function which is not mutable neither.

@aluanhaddad
Copy link
Contributor

aluanhaddad commented Oct 27, 2016

@akarzazi A value of something of the type typeof Calculator1 would be a typeof Calculator1. For example the value Calculator1 is a typeof Calculator1. That value is mutable.

@akarzazi
Copy link
Author

@aluanhaddad
We cannot call something mutable, if there is only one value assignable to it.
The same goes for : type "a".
Some code to illustrate:

interface interface1 {
    discriminant: "a"
}

var obj: interface1;
obj.discriminant = "a";
obj.discriminant = <any>1
// all ok

Furthermore , I don't think mutability has something todo with type narrowing.
As you can see, narrowing already works fine on shapes (from my previous sample) :

function testType(a: typeof Calculator1 | typeof Calculator2) {
    if (a === Calculator2) {
        a. // is typeof Calculator2 , good ! 
       // Type narrowing works fine !  
    }
}

Why the same narrowing won't work for union type discrimination ?

@mhegazy mhegazy closed this as completed Apr 24, 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
Design Limitation Constraints of the existing architecture prevent this from being fixed Out of Scope This idea sits outside of the TypeScript language design constraints Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

3 participants