Skip to content

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

Description

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.

Activity

  1. ahejlsberg commented on Sep 19, 2016

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

  2. Swatinem commented on Sep 19, 2016

    @Swatinem
    ContributorAuthor

    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.

  3. ahejlsberg commented on Sep 19, 2016

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

  4. 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

    Working as IntendedThe behavior described is the intended behavior; this is not a bug

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions