Skip to content

Constants not being inferred as expectedt #10416

Closed

Description

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.

Activity

  1. changed the title [-]Constants now being inferred as expected[/-] [+]Constants not being inferred as expectedt[/+] on Aug 18, 2016
  2. mhegazy commented on Aug 18, 2016

    @mhegazy
    Contributor

    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"
  3. kitsonk commented on Aug 18, 2016

    @kitsonk
    Contributor

    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.

  4. kastermester commented on Aug 18, 2016

    @kastermester
    Author

    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.

  5. DanielRosenwasser commented on Aug 18, 2016

    @DanielRosenwasser
    Member

    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.

  6. kastermester commented on Aug 18, 2016

    @kastermester
    Author

    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.

  7. ahejlsberg commented on Aug 18, 2016

    @ahejlsberg
    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.

  8. locked and limited conversation to collaborators on Jun 19, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Design LimitationConstraints of the existing architecture prevent this from being fixed

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions