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

strictNullChecks non-null assertion lost inside Array#map callback #10982

Closed
Swatinem opened this issue Sep 19, 2016 · 3 comments
Closed

strictNullChecks non-null assertion lost inside Array#map callback #10982

Swatinem opened this issue Sep 19, 2016 · 3 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@Swatinem
Copy link
Contributor

TypeScript Version: nightly (2.1.0-dev.20160919)

Code

function f(_: string) {}

const foo: {bar: null | string} = {bar: "foo"};

if (foo.bar) {
    foo.bar.length; // works fine

    (function () {
        // works fine
        foo.bar.length;
        f(foo.bar);
    })();

    // both fail non-null assertion
    [].map(_ => {
        foo.bar.length;
        f(foo.bar);
    });
}

Expected behavior:

There should be no compile errors, as foo.bar is guaranteed to be non-null inside the whole block

Actual behavior:

Inside the map callback, TS thinks that foo.bar can be null. Strangely enough, an IIFE works just fine.

@ahejlsberg
Copy link
Member

This is working as intended. The type checker knows that foo.bar is non-null at the time the callback function is created, but it doesn't know that foo.bar is non-null when the callback is called (because bar is a mutable property that could be changed before or between calls to the callback function). It works with the IIFE because the type checker can see that it is immediately invoked. If you remove the last () from the IIFE you will indeed get the same error.

The suggested way to do this is to copy foo.bar into a const local and then use that in the callback.

Also see #8849.

@ahejlsberg ahejlsberg added the Working as Intended The behavior described is the intended behavior; this is not a bug label Sep 19, 2016
@Swatinem
Copy link
Contributor Author

Thanks for the explanation. So the proper fix here would be to teach the type checker that Array#map/forEach are just sugar for a loop, similar to how an IIFE is just sugar for a block.

@ahejlsberg
Copy link
Member

So the proper fix here would be to teach the type checker that Array#map/forEach are just sugar for a loop, similar to how an IIFE is just sugar for a block.

Well, possibly. Even knowing that the callback is only called before map returns, there are still several different ways it could be called. For example, it might never be called, it might be called only once, or it might be called multiple times. Each would have different effects on control flow analysis. Presumably you'd need a way to annotate for all of the above such that it isn't just limited to Array#map and Array#forEach. It would amount to a fair bit of complexity.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

3 participants