Skip to content

Constants not being inferred as expectedt #10416

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Closed
kastermester opened this issue Aug 18, 2016 · 6 comments
Closed

Constants not being inferred as expectedt #10416

kastermester opened this issue Aug 18, 2016 · 6 comments
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed

Comments

@kastermester
Copy link

kastermester commented Aug 18, 2016

TypeScript Version: 2.0.0

Code

function f(x: string | number) : 'string' | 'number' {
    let res = 'string';
    // res: 'string'
    if (typeof(x) === 'number') {
        res = 'number';
        // res: 'number'
    }
    // res: 'string' | 'number'

    return res;
}

Expected behavior:
No type error
Actual behavior:
Type error on the last line in the function. I believe this is due to the first assignment of res being inferred as type string. Which means the return type is type string. Instead I would have expected the type to be inferred as 'string', and then widened to 'string'|'number' inside the if block and after the if block - thus producing a correct type in the return statement.

I will now admit I have not looked that thorough for other similiar bug reports - as there are a lot of issues in the repo. I looked through the first couple of pages though. If this is a duplicate, my apologies.

@kastermester kastermester changed the title Constants now being inferred as expected Constants not being inferred as expectedt Aug 18, 2016
@mhegazy mhegazy added the Design Limitation Constraints of the existing architecture prevent this from being fixed label Aug 18, 2016
@mhegazy
Copy link
Contributor

mhegazy commented Aug 18, 2016

literal types are only inferred in the presence of a contextual type. so "string" in the example above is always inferred as string and not as a literal type "string". if the declaration however was written as let res: "string" = "string" or using a type alias as let res: NumberOrString = "string" it would have been inferred correctly.

the rational about this behavior is that literal types, as well as tuple types, were added in the language in later versions. and inferring strings as literal types automatically would be a breaking change, e.g.

var x = "foo";
x = "bar"; // would be an error if x's type was inferred as "foo"

@kitsonk
Copy link
Contributor

kitsonk commented Aug 18, 2016

inferring strings as literal types automatically would be a breaking change

I would say a likely undesirable one too. Contextually inferring literal types sounds super scary to me, even if it were opt-in. Being explicit about literal types, sounds a lot more sound to me. Inferring tuples could be useful, though they obviously conflict with array inference.

@kastermester
Copy link
Author

kastermester commented Aug 18, 2016

@mhegazy Why could it not just alter the type? I get it is not that useful in your example, but - I have not provided it with an explicit type - so at the first line it would be 'foo', second line 'bar' (which are both valid string values). I have updated my example to note which types I would expect to be inferred at which points in the program.

@DanielRosenwasser
Copy link
Member

DanielRosenwasser commented Aug 18, 2016

Right now if you want to change the type, you can only narrow a type. Widening is a different concept entirely.

So if you can only narrow at this moment, the initial value of x needs to be inferred differently. We don't currently do type inference on locals anywhere other than the initializer. If that changes, we can experiment with that idea.

@kastermester
Copy link
Author

kastermester commented Aug 18, 2016

I see :)

FYI I'm experimenting with converting a flow project to TypeScript and this one came up. It is one of the few features flow has that makes code work very naturally and as expected. Due to the newly added control flow analysis in TypeScript I figured this was supposed to work as is, as it seems like a natural next step to me.

I get that code like this, due to the natural comparison to a type system like C#, would probably need to be illegal:

let x = 'string';
x = 5;

But personally I would have expected string constants like this to flow naturally through the program.

But thanks for the feedback :) Feel free to close this if you see this as a non-issue.

@ahejlsberg
Copy link
Member

FWIW, this is definitely an issue we continue to think about. TypeScript generally has the rule that a variable's type is determined at it's declaration and control flow analysis only narrows the declared type (e.g. by refining a declared type of string | undefined to just string). We actually have all of the machinery in place to only infer from control flow analysis, but we need to work it into the system in a way that doesn't break backwards compatibility.

@mhegazy mhegazy closed this as completed Dec 29, 2016
@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
# for free to subscribe to this conversation on GitHub. Already have an account? #.
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed
Projects
None yet
Development

No branches or pull requests

5 participants