You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.
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.
varx="foo";x="bar";// would be an error if x's type was inferred as "foo"
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.
Mohamed Hegazy (@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.
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.
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.
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.
Activity
[-]Constants now being inferred as expected[/-][+]Constants not being inferred as expectedt[/+]mhegazy commentedon Aug 18, 2016
literal types are only inferred in the presence of a contextual type. so
"string"in the example above is always inferred asstringand not as a literal type"string". if the declaration however was written aslet res: "string" = "string"or using a type alias aslet 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.
kitsonk commentedon Aug 18, 2016
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 commentedon Aug 18, 2016
Mohamed Hegazy (@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 validstringvalues). I have updated my example to note which types I would expect to be inferred at which points in the program.DanielRosenwasser commentedon 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
xneeds 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 commentedon 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:
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 commentedon Aug 18, 2016
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 | undefinedto juststring). 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.