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

More optional chaining control flow analysis #34597

Merged
merged 5 commits into from
Oct 21, 2019

Conversation

ahejlsberg
Copy link
Member

@ahejlsberg ahejlsberg commented Oct 19, 2019

With this PR we expand on #33821 to reflect more effects of optional chaining in control flow analysis. Some examples:

declare function assert(x: unknown): asserts x;
declare function assertNonNull<T>(x: T): asserts x is NonNullable<T>;

type Thing = { foo?: string | number, bar(): number };

function foo(o: Thing | undefined, value: number) {
    if (o?.foo === value) {
        o;  // Thing
        o.foo;  // number
    }
    if (typeof o?.foo === "string") {
        o;  // Thing
        o.foo;  // string
    }
    switch (o?.foo) {
        case value:
            o;  // Thing
            o.foo;  // number
    }
    switch (typeof o?.foo) {
        case "string":
            o;  // Thing
            o.foo;  // string
    }
    if (!!true) {
        assert(typeof o?.foo === "string");
        o;  // Thing
        o.foo;  // string
    }
    if (!!true) {
        assertNonNull(o?.foo);
        o;  // Thing
        o.foo;  // string | number
    }
}

The complete list of optional chaining constructs with control flow effects in --strictNullChecks mode is now:

  • in code guarded by o?.foo === value, where the type of value doesn't include undefined, we remove undefined and null from the type of o in the true branch, and
  • in code guarded by o?.foo !== value, where the type of value doesn't include undefined, we remove undefined and null from the type of o in the false branch, and
  • in code guarded by o?.foo == value, where the type of value doesn't include undefined or null, we remove undefined and null from the type of o in the true branch, and
  • in code guarded by o?.foo != value, where the type of value doesn't include undefined or null, we remove undefined and null from the type of o in the false branch, and
  • in code guarded by o?.foo === undefined, o?.foo == undefined or o?.foo == null we remove undefined and null from the type of o in the false branch, and
  • in code guarded by o?.foo !== undefined, o?.foo != undefined or o?.foo != null we remove undefined and null from the type of o in the true branch, and
  • in code guarded by typeof o?.foo === "xxx" or typeof o?.foo == "xxx", where "xxx" is not "undefined", we remove undefined and null from the type of o in the true branch, and
  • in code guarded by typeof o?.foo !== "xxx" or typeof o?.foo != "xxx", where "xxx" is not "undefined", we remove undefined and null from the type of o in the false branch, and
  • in code guarded by typeof o?.foo === "undefined" or typeof o?.foo == "undefined" we remove undefined and null from the type of o in the false branch, and
  • in code guarded by typeof o?.foo !== "undefined" or typeof o?.foo != "undefined" we remove undefined and null from the type of o in the true branch, and
  • in code guarded by o?.foo instanceof xxx we remove undefined and null from the type of o in the true branch, and
  • in a case xxx block of a switch (o?.foo) statement we remove undefined and null from the type of o provided the type of xxx doesn't include undefined, and
  • in a case xxx block of a switch (typeof o?.foo) statement we remove undefined and null from the type of o provided xxx isn't "undefined", and
  • in code following a top-level call expression assertIsXXX(o?.foo), where assertIsXXX is declared with an asserts x is XXX type predicate and type XXX doesn't include undefined, we remove undefined and null from the type of o, and
  • in code following a top-level call expression assert(xxx), where assert is declared with an asserts x type predicate, we reflect the false branch effects of the logical expression xxx, including effects from optional property access chains.

Fixes #34570.

@ahejlsberg ahejlsberg added this to the TypeScript 3.7.1 milestone Oct 19, 2019
@ahejlsberg ahejlsberg changed the title More optional control flow analysis More optional chaining control flow analysis Oct 19, 2019
@ahejlsberg ahejlsberg merged commit ff6626f into master Oct 21, 2019
@ahejlsberg ahejlsberg deleted the optionalChainControlFlow branch October 21, 2019 19:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Optional chaining control flow refinements missing
2 participants