Skip to content

More optional chaining control flow analysis#34597

Merged
ahejlsberg merged 5 commits intomasterfrom
optionalChainControlFlow
Oct 21, 2019
Merged

More optional chaining control flow analysis#34597
ahejlsberg merged 5 commits intomasterfrom
optionalChainControlFlow

Conversation

@ahejlsberg
Copy link
Copy Markdown
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
@microsoft microsoft locked as resolved and limited conversation to collaborators Oct 21, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Optional chaining control flow refinements missing

2 participants