From 719b779b6bb035e70b8603fc51c8f6fe1a196ef0 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 4 Oct 2019 15:27:32 -0700 Subject: [PATCH 1/8] Reflect CFA effects of optional chains in equality checks --- src/compiler/checker.ts | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6ece23f022ab3..5f0f8241a5eca 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -18124,6 +18124,16 @@ namespace ts { return false; } + function optionalChainContainsReference(source: Node, target: Node) { + while (isOptionalChain(source)) { + source = source.expression; + if (isMatchingReference(source, target)) { + return true; + } + } + return false; + } + // Return true if target is a property access xxx.yyy, source is a property access xxx.zzz, the declared // type of xxx is a union type, and yyy is a property that is possibly a discriminant. We consider a property // a possible discriminant if its type differs in the constituents of containing union type, and if every @@ -19350,6 +19360,14 @@ namespace ts { if (isMatchingReference(reference, right)) { return narrowTypeByEquality(type, operator, left, assumeTrue); } + if (assumeTrue && (operator === SyntaxKind.EqualsEqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken)) { + if (optionalChainContainsReference(left, reference)) { + type = narrowTypeByOptionalChainContainment(type, operator, right); + } + else if (optionalChainContainsReference(right, reference)) { + type = narrowTypeByOptionalChainContainment(type, operator, left); + } + } if (isMatchingReferenceDiscriminant(left, declaredType)) { return narrowTypeByDiscriminant(type, left, t => narrowTypeByEquality(t, operator, right, assumeTrue)); } @@ -19374,6 +19392,16 @@ namespace ts { return type; } + function narrowTypeByOptionalChainContainment(type: Type, operator: SyntaxKind, value: Expression): Type { + // We are in the true branch of obj?.foo === value or obj?.foo !== value. We remove undefined and null from + // the type of obj if (a) the operator is === and the type of value doesn't include undefined or (b) the + // operator is !== and the type of value is undefined. + const valueType = getTypeOfExpression(value); + return operator === SyntaxKind.EqualsEqualsEqualsToken && !(getTypeFacts(valueType) & TypeFacts.EQUndefined) || + operator === SyntaxKind.ExclamationEqualsEqualsToken && valueType.flags & TypeFlags.Undefined ? + getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull) : type; + } + function narrowTypeByEquality(type: Type, operator: SyntaxKind, value: Expression, assumeTrue: boolean): Type { if (type.flags & TypeFlags.Any) { return type; From 2447bd775f4c33667e7f8ce7edd4b7da00d0870f Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 4 Oct 2019 16:01:09 -0700 Subject: [PATCH 2/8] Update tests --- .../cases/conformance/controlFlow/controlFlowOptionalChain.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts b/tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts index dadb1b36076db..dabeea617f4d0 100644 --- a/tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts +++ b/tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts @@ -61,8 +61,8 @@ o2.f; declare const o3: { x: 1, y: string } | { x: 2, y: number } | undefined; if (o3?.x === 1) { - o3; // TODO: should be `{ x: y, y: string }` - o3.x; // TODO: should not be an error. + o3; + o3.x; o3?.x; } else { From 7d32355f957dd5547aa0a7a50d1a1c2a56d90bf8 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 4 Oct 2019 16:01:27 -0700 Subject: [PATCH 3/8] Accept new baselines --- .../reference/controlFlowOptionalChain.errors.txt | 9 +++------ .../reference/controlFlowOptionalChain.js | 8 ++++---- .../reference/controlFlowOptionalChain.symbols | 12 ++++++------ .../reference/controlFlowOptionalChain.types | 14 +++++++------- 4 files changed, 20 insertions(+), 23 deletions(-) diff --git a/tests/baselines/reference/controlFlowOptionalChain.errors.txt b/tests/baselines/reference/controlFlowOptionalChain.errors.txt index ce46b232b3d81..0d2d268d59165 100644 --- a/tests/baselines/reference/controlFlowOptionalChain.errors.txt +++ b/tests/baselines/reference/controlFlowOptionalChain.errors.txt @@ -6,7 +6,6 @@ tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(35,5): error TS2 tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(39,1): error TS2722: Cannot invoke an object which is possibly 'undefined'. tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(52,5): error TS2532: Object is possibly 'undefined'. tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(57,1): error TS2532: Object is possibly 'undefined'. -tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(62,5): error TS2532: Object is possibly 'undefined'. tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(68,5): error TS2532: Object is possibly 'undefined'. tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(72,1): error TS2532: Object is possibly 'undefined'. tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(83,5): error TS2532: Object is possibly 'undefined'. @@ -22,7 +21,7 @@ tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(134,1): error TS tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(153,9): error TS2775: Assertions require every name in the call target to be declared with an explicit type annotation. -==== tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts (22 errors) ==== +==== tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts (21 errors) ==== // assignments in shortcutting chain declare const o: undefined | { [key: string]: any; @@ -99,10 +98,8 @@ tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(153,9): error TS declare const o3: { x: 1, y: string } | { x: 2, y: number } | undefined; if (o3?.x === 1) { - o3; // TODO: should be `{ x: y, y: string }` - o3.x; // TODO: should not be an error. - ~~ -!!! error TS2532: Object is possibly 'undefined'. + o3; + o3.x; o3?.x; } else { diff --git a/tests/baselines/reference/controlFlowOptionalChain.js b/tests/baselines/reference/controlFlowOptionalChain.js index 58a2873f93aa1..fed8131681f08 100644 --- a/tests/baselines/reference/controlFlowOptionalChain.js +++ b/tests/baselines/reference/controlFlowOptionalChain.js @@ -59,8 +59,8 @@ o2.f; declare const o3: { x: 1, y: string } | { x: 2, y: number } | undefined; if (o3?.x === 1) { - o3; // TODO: should be `{ x: y, y: string }` - o3.x; // TODO: should not be an error. + o3; + o3.x; o3?.x; } else { @@ -206,8 +206,8 @@ o2; (_k = o2) === null || _k === void 0 ? void 0 : _k.f; o2.f; if (((_l = o3) === null || _l === void 0 ? void 0 : _l.x) === 1) { - o3; // TODO: should be `{ x: y, y: string }` - o3.x; // TODO: should not be an error. + o3; + o3.x; (_m = o3) === null || _m === void 0 ? void 0 : _m.x; } else { diff --git a/tests/baselines/reference/controlFlowOptionalChain.symbols b/tests/baselines/reference/controlFlowOptionalChain.symbols index 21f1de2a41026..d3390729ac424 100644 --- a/tests/baselines/reference/controlFlowOptionalChain.symbols +++ b/tests/baselines/reference/controlFlowOptionalChain.symbols @@ -182,18 +182,18 @@ if (o3?.x === 1) { >o3 : Symbol(o3, Decl(controlFlowOptionalChain.ts, 58, 13)) >x : Symbol(x, Decl(controlFlowOptionalChain.ts, 58, 19), Decl(controlFlowOptionalChain.ts, 58, 41)) - o3; // TODO: should be `{ x: y, y: string }` + o3; >o3 : Symbol(o3, Decl(controlFlowOptionalChain.ts, 58, 13)) - o3.x; // TODO: should not be an error. ->o3.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 58, 19), Decl(controlFlowOptionalChain.ts, 58, 41)) + o3.x; +>o3.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 58, 19)) >o3 : Symbol(o3, Decl(controlFlowOptionalChain.ts, 58, 13)) ->x : Symbol(x, Decl(controlFlowOptionalChain.ts, 58, 19), Decl(controlFlowOptionalChain.ts, 58, 41)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 58, 19)) o3?.x; ->o3?.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 58, 19), Decl(controlFlowOptionalChain.ts, 58, 41)) +>o3?.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 58, 19)) >o3 : Symbol(o3, Decl(controlFlowOptionalChain.ts, 58, 13)) ->x : Symbol(x, Decl(controlFlowOptionalChain.ts, 58, 19), Decl(controlFlowOptionalChain.ts, 58, 41)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 58, 19)) } else { o3; diff --git a/tests/baselines/reference/controlFlowOptionalChain.types b/tests/baselines/reference/controlFlowOptionalChain.types index a93c5b0b43f04..5b9d10b219810 100644 --- a/tests/baselines/reference/controlFlowOptionalChain.types +++ b/tests/baselines/reference/controlFlowOptionalChain.types @@ -208,18 +208,18 @@ if (o3?.x === 1) { >x : 1 | 2 | undefined >1 : 1 - o3; // TODO: should be `{ x: y, y: string }` ->o3 : { x: 1; y: string; } | { x: 2; y: number; } | undefined + o3; +>o3 : { x: 1; y: string; } - o3.x; // TODO: should not be an error. + o3.x; >o3.x : 1 ->o3 : { x: 1; y: string; } | { x: 2; y: number; } | undefined +>o3 : { x: 1; y: string; } >x : 1 o3?.x; ->o3?.x : 1 | undefined ->o3 : { x: 1; y: string; } | { x: 2; y: number; } | undefined ->x : 1 | undefined +>o3?.x : 1 +>o3 : { x: 1; y: string; } +>x : 1 } else { o3; From 42500d060644d07f5884c086ba1db46bc06a8419 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 4 Oct 2019 16:12:44 -0700 Subject: [PATCH 4/8] Add tests --- .../controlFlow/controlFlowOptionalChain.ts | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts b/tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts index dabeea617f4d0..d04fdb9e1b0bc 100644 --- a/tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts +++ b/tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts @@ -161,3 +161,80 @@ function f01(x: unknown) { x; } } + +type Thing = { foo: number, bar(): number }; + +function f10(o: Thing | undefined, value: number) { + if (o?.foo === value) { + o; + o.foo; + } + if (o?.["foo"] === value) { + o; + o["foo"]; + } + if (o?.bar() === value) { + o; + o.bar; + } +} + +function f11(o: Thing | null, value: number) { + if (o?.foo === value) { + o; + o.foo; + } + if (o?.["foo"] === value) { + o; + o["foo"]; + } + if (o?.bar() === value) { + o; + o.bar; + } +} + +function f12(o: Thing | undefined, value: number | undefined) { + if (o?.foo === value) { + o; + o.foo; // Error + } + if (o?.["foo"] === value) { + o; + o["foo"]; // Error + } + if (o?.bar() === value) { + o; + o.bar; // Error + } +} + +function f13(o: Thing | undefined) { + if (o?.foo !== undefined) { + o; + o.foo; + } + if (o?.["foo"] !== undefined) { + o; + o["foo"]; + } + if (o?.bar() !== undefined) { + o; + o.bar; + } +} + +function f14(o: Thing | null) { + if (o?.foo !== undefined) { + o; + o.foo; + } + if (o?.["foo"] !== undefined) { + o; + o["foo"]; + } + if (o?.bar() !== undefined) { + o; + o.bar; + } +} From 464d1a74c734cab6fed95e3f2245befa8d634f4b Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 4 Oct 2019 16:12:53 -0700 Subject: [PATCH 5/8] Accept new baselines --- .../controlFlowOptionalChain.errors.txt | 88 +++++- .../reference/controlFlowOptionalChain.js | 152 ++++++++++ .../controlFlowOptionalChain.symbols | 237 +++++++++++++++ .../reference/controlFlowOptionalChain.types | 270 ++++++++++++++++++ 4 files changed, 746 insertions(+), 1 deletion(-) diff --git a/tests/baselines/reference/controlFlowOptionalChain.errors.txt b/tests/baselines/reference/controlFlowOptionalChain.errors.txt index 0d2d268d59165..1c5c4b18399b5 100644 --- a/tests/baselines/reference/controlFlowOptionalChain.errors.txt +++ b/tests/baselines/reference/controlFlowOptionalChain.errors.txt @@ -19,9 +19,12 @@ tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(112,1): error TS tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(130,5): error TS2532: Object is possibly 'undefined'. tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(134,1): error TS2532: Object is possibly 'undefined'. tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(153,9): error TS2775: Assertions require every name in the call target to be declared with an explicit type annotation. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(197,9): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(201,9): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(205,9): error TS2532: Object is possibly 'undefined'. -==== tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts (21 errors) ==== +==== tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts (24 errors) ==== // assignments in shortcutting chain declare const o: undefined | { [key: string]: any; @@ -224,4 +227,87 @@ tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(153,9): error TS x; } } + + type Thing = { foo: number, bar(): number }; + + function f10(o: Thing | undefined, value: number) { + if (o?.foo === value) { + o; + o.foo; + } + if (o?.["foo"] === value) { + o; + o["foo"]; + } + if (o?.bar() === value) { + o; + o.bar; + } + } + + function f11(o: Thing | null, value: number) { + if (o?.foo === value) { + o; + o.foo; + } + if (o?.["foo"] === value) { + o; + o["foo"]; + } + if (o?.bar() === value) { + o; + o.bar; + } + } + + function f12(o: Thing | undefined, value: number | undefined) { + if (o?.foo === value) { + o; + o.foo; // Error + ~ +!!! error TS2532: Object is possibly 'undefined'. + } + if (o?.["foo"] === value) { + o; + o["foo"]; // Error + ~ +!!! error TS2532: Object is possibly 'undefined'. + } + if (o?.bar() === value) { + o; + o.bar; // Error + ~ +!!! error TS2532: Object is possibly 'undefined'. + } + } + + function f13(o: Thing | undefined) { + if (o?.foo !== undefined) { + o; + o.foo; + } + if (o?.["foo"] !== undefined) { + o; + o["foo"]; + } + if (o?.bar() !== undefined) { + o; + o.bar; + } + } + + function f14(o: Thing | null) { + if (o?.foo !== undefined) { + o; + o.foo; + } + if (o?.["foo"] !== undefined) { + o; + o["foo"]; + } + if (o?.bar() !== undefined) { + o; + o.bar; + } + } \ No newline at end of file diff --git a/tests/baselines/reference/controlFlowOptionalChain.js b/tests/baselines/reference/controlFlowOptionalChain.js index fed8131681f08..9b42d764726bf 100644 --- a/tests/baselines/reference/controlFlowOptionalChain.js +++ b/tests/baselines/reference/controlFlowOptionalChain.js @@ -159,6 +159,83 @@ function f01(x: unknown) { x; } } + +type Thing = { foo: number, bar(): number }; + +function f10(o: Thing | undefined, value: number) { + if (o?.foo === value) { + o; + o.foo; + } + if (o?.["foo"] === value) { + o; + o["foo"]; + } + if (o?.bar() === value) { + o; + o.bar; + } +} + +function f11(o: Thing | null, value: number) { + if (o?.foo === value) { + o; + o.foo; + } + if (o?.["foo"] === value) { + o; + o["foo"]; + } + if (o?.bar() === value) { + o; + o.bar; + } +} + +function f12(o: Thing | undefined, value: number | undefined) { + if (o?.foo === value) { + o; + o.foo; // Error + } + if (o?.["foo"] === value) { + o; + o["foo"]; // Error + } + if (o?.bar() === value) { + o; + o.bar; // Error + } +} + +function f13(o: Thing | undefined) { + if (o?.foo !== undefined) { + o; + o.foo; + } + if (o?.["foo"] !== undefined) { + o; + o["foo"]; + } + if (o?.bar() !== undefined) { + o; + o.bar; + } +} + +function f14(o: Thing | null) { + if (o?.foo !== undefined) { + o; + o.foo; + } + if (o?.["foo"] !== undefined) { + o; + o["foo"]; + } + if (o?.bar() !== undefined) { + o; + o.bar; + } +} //// [controlFlowOptionalChain.js] @@ -286,3 +363,78 @@ function f01(x) { x; } } +function f10(o, value) { + var _a, _b, _c; + if (((_a = o) === null || _a === void 0 ? void 0 : _a.foo) === value) { + o; + o.foo; + } + if (((_b = o) === null || _b === void 0 ? void 0 : _b["foo"]) === value) { + o; + o["foo"]; + } + if (((_c = o) === null || _c === void 0 ? void 0 : _c.bar()) === value) { + o; + o.bar; + } +} +function f11(o, value) { + var _a, _b, _c; + if (((_a = o) === null || _a === void 0 ? void 0 : _a.foo) === value) { + o; + o.foo; + } + if (((_b = o) === null || _b === void 0 ? void 0 : _b["foo"]) === value) { + o; + o["foo"]; + } + if (((_c = o) === null || _c === void 0 ? void 0 : _c.bar()) === value) { + o; + o.bar; + } +} +function f12(o, value) { + var _a, _b, _c; + if (((_a = o) === null || _a === void 0 ? void 0 : _a.foo) === value) { + o; + o.foo; // Error + } + if (((_b = o) === null || _b === void 0 ? void 0 : _b["foo"]) === value) { + o; + o["foo"]; // Error + } + if (((_c = o) === null || _c === void 0 ? void 0 : _c.bar()) === value) { + o; + o.bar; // Error + } +} +function f13(o) { + var _a, _b, _c; + if (((_a = o) === null || _a === void 0 ? void 0 : _a.foo) !== undefined) { + o; + o.foo; + } + if (((_b = o) === null || _b === void 0 ? void 0 : _b["foo"]) !== undefined) { + o; + o["foo"]; + } + if (((_c = o) === null || _c === void 0 ? void 0 : _c.bar()) !== undefined) { + o; + o.bar; + } +} +function f14(o) { + var _a, _b, _c; + if (((_a = o) === null || _a === void 0 ? void 0 : _a.foo) !== undefined) { + o; + o.foo; + } + if (((_b = o) === null || _b === void 0 ? void 0 : _b["foo"]) !== undefined) { + o; + o["foo"]; + } + if (((_c = o) === null || _c === void 0 ? void 0 : _c.bar()) !== undefined) { + o; + o.bar; + } +} diff --git a/tests/baselines/reference/controlFlowOptionalChain.symbols b/tests/baselines/reference/controlFlowOptionalChain.symbols index d3390729ac424..495e6371a6330 100644 --- a/tests/baselines/reference/controlFlowOptionalChain.symbols +++ b/tests/baselines/reference/controlFlowOptionalChain.symbols @@ -600,3 +600,240 @@ function f01(x: unknown) { } } +type Thing = { foo: number, bar(): number }; +>Thing : Symbol(Thing, Decl(controlFlowOptionalChain.ts, 159, 1)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 27)) + +function f10(o: Thing | undefined, value: number) { +>f10 : Symbol(f10, Decl(controlFlowOptionalChain.ts, 161, 44)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 163, 13)) +>Thing : Symbol(Thing, Decl(controlFlowOptionalChain.ts, 159, 1)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 163, 34)) + + if (o?.foo === value) { +>o?.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 163, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 163, 34)) + + o; +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 163, 13)) + + o.foo; +>o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 163, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + if (o?.["foo"] === value) { +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 163, 13)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 163, 34)) + + o; +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 163, 13)) + + o["foo"]; +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 163, 13)) +>"foo" : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + if (o?.bar() === value) { +>o?.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 27)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 163, 13)) +>bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 27)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 163, 34)) + + o; +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 163, 13)) + + o.bar; +>o.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 27)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 163, 13)) +>bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 27)) + } +} + +function f11(o: Thing | null, value: number) { +>f11 : Symbol(f11, Decl(controlFlowOptionalChain.ts, 176, 1)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 178, 13)) +>Thing : Symbol(Thing, Decl(controlFlowOptionalChain.ts, 159, 1)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 178, 29)) + + if (o?.foo === value) { +>o?.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 178, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 178, 29)) + + o; +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 178, 13)) + + o.foo; +>o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 178, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + if (o?.["foo"] === value) { +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 178, 13)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 178, 29)) + + o; +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 178, 13)) + + o["foo"]; +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 178, 13)) +>"foo" : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + if (o?.bar() === value) { +>o?.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 27)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 178, 13)) +>bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 27)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 178, 29)) + + o; +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 178, 13)) + + o.bar; +>o.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 27)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 178, 13)) +>bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 27)) + } +} + +function f12(o: Thing | undefined, value: number | undefined) { +>f12 : Symbol(f12, Decl(controlFlowOptionalChain.ts, 191, 1)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 193, 13)) +>Thing : Symbol(Thing, Decl(controlFlowOptionalChain.ts, 159, 1)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 193, 34)) + + if (o?.foo === value) { +>o?.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 193, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 193, 34)) + + o; +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 193, 13)) + + o.foo; // Error +>o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 193, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + if (o?.["foo"] === value) { +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 193, 13)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 193, 34)) + + o; +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 193, 13)) + + o["foo"]; // Error +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 193, 13)) + } + if (o?.bar() === value) { +>o?.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 27)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 193, 13)) +>bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 27)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 193, 34)) + + o; +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 193, 13)) + + o.bar; // Error +>o.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 27)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 193, 13)) +>bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 27)) + } +} + +function f13(o: Thing | undefined) { +>f13 : Symbol(f13, Decl(controlFlowOptionalChain.ts, 206, 1)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 208, 13)) +>Thing : Symbol(Thing, Decl(controlFlowOptionalChain.ts, 159, 1)) + + if (o?.foo !== undefined) { +>o?.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 208, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>undefined : Symbol(undefined) + + o; +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 208, 13)) + + o.foo; +>o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 208, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + if (o?.["foo"] !== undefined) { +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 208, 13)) +>undefined : Symbol(undefined) + + o; +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 208, 13)) + + o["foo"]; +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 208, 13)) +>"foo" : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + if (o?.bar() !== undefined) { +>o?.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 27)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 208, 13)) +>bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 27)) +>undefined : Symbol(undefined) + + o; +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 208, 13)) + + o.bar; +>o.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 27)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 208, 13)) +>bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 27)) + } +} + +function f14(o: Thing | null) { +>f14 : Symbol(f14, Decl(controlFlowOptionalChain.ts, 221, 1)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 223, 13)) +>Thing : Symbol(Thing, Decl(controlFlowOptionalChain.ts, 159, 1)) + + if (o?.foo !== undefined) { +>o?.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 223, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>undefined : Symbol(undefined) + + o; +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 223, 13)) + + o.foo; +>o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 223, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + if (o?.["foo"] !== undefined) { +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 223, 13)) +>undefined : Symbol(undefined) + + o; +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 223, 13)) + + o["foo"]; +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 223, 13)) +>"foo" : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + if (o?.bar() !== undefined) { +>o?.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 27)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 223, 13)) +>bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 27)) +>undefined : Symbol(undefined) + + o; +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 223, 13)) + + o.bar; +>o.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 27)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 223, 13)) +>bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 27)) + } +} + diff --git a/tests/baselines/reference/controlFlowOptionalChain.types b/tests/baselines/reference/controlFlowOptionalChain.types index 5b9d10b219810..0c243dc6be68b 100644 --- a/tests/baselines/reference/controlFlowOptionalChain.types +++ b/tests/baselines/reference/controlFlowOptionalChain.types @@ -634,3 +634,273 @@ function f01(x: unknown) { } } +type Thing = { foo: number, bar(): number }; +>Thing : Thing +>foo : number +>bar : () => number + +function f10(o: Thing | undefined, value: number) { +>f10 : (o: Thing | undefined, value: number) => void +>o : Thing | undefined +>value : number + + if (o?.foo === value) { +>o?.foo === value : boolean +>o?.foo : number | undefined +>o : Thing | undefined +>foo : number | undefined +>value : number + + o; +>o : Thing + + o.foo; +>o.foo : number +>o : Thing +>foo : number + } + if (o?.["foo"] === value) { +>o?.["foo"] === value : boolean +>o?.["foo"] : number | undefined +>o : Thing | undefined +>"foo" : "foo" +>value : number + + o; +>o : Thing + + o["foo"]; +>o["foo"] : number +>o : Thing +>"foo" : "foo" + } + if (o?.bar() === value) { +>o?.bar() === value : boolean +>o?.bar() : number | undefined +>o?.bar : (() => number) | undefined +>o : Thing | undefined +>bar : (() => number) | undefined +>value : number + + o; +>o : Thing + + o.bar; +>o.bar : () => number +>o : Thing +>bar : () => number + } +} + +function f11(o: Thing | null, value: number) { +>f11 : (o: Thing | null, value: number) => void +>o : Thing | null +>null : null +>value : number + + if (o?.foo === value) { +>o?.foo === value : boolean +>o?.foo : number | undefined +>o : Thing | null +>foo : number | undefined +>value : number + + o; +>o : Thing + + o.foo; +>o.foo : number +>o : Thing +>foo : number + } + if (o?.["foo"] === value) { +>o?.["foo"] === value : boolean +>o?.["foo"] : number | undefined +>o : Thing | null +>"foo" : "foo" +>value : number + + o; +>o : Thing + + o["foo"]; +>o["foo"] : number +>o : Thing +>"foo" : "foo" + } + if (o?.bar() === value) { +>o?.bar() === value : boolean +>o?.bar() : number | undefined +>o?.bar : (() => number) | undefined +>o : Thing | null +>bar : (() => number) | undefined +>value : number + + o; +>o : Thing + + o.bar; +>o.bar : () => number +>o : Thing +>bar : () => number + } +} + +function f12(o: Thing | undefined, value: number | undefined) { +>f12 : (o: Thing | undefined, value: number | undefined) => void +>o : Thing | undefined +>value : number | undefined + + if (o?.foo === value) { +>o?.foo === value : boolean +>o?.foo : number | undefined +>o : Thing | undefined +>foo : number | undefined +>value : number | undefined + + o; +>o : Thing | undefined + + o.foo; // Error +>o.foo : number +>o : Thing | undefined +>foo : number + } + if (o?.["foo"] === value) { +>o?.["foo"] === value : boolean +>o?.["foo"] : number | undefined +>o : Thing | undefined +>"foo" : "foo" +>value : number | undefined + + o; +>o : Thing | undefined + + o["foo"]; // Error +>o["foo"] : number +>o : Thing | undefined +>"foo" : "foo" + } + if (o?.bar() === value) { +>o?.bar() === value : boolean +>o?.bar() : number | undefined +>o?.bar : (() => number) | undefined +>o : Thing | undefined +>bar : (() => number) | undefined +>value : number | undefined + + o; +>o : Thing | undefined + + o.bar; // Error +>o.bar : () => number +>o : Thing | undefined +>bar : () => number + } +} + +function f13(o: Thing | undefined) { +>f13 : (o: Thing | undefined) => void +>o : Thing | undefined + + if (o?.foo !== undefined) { +>o?.foo !== undefined : boolean +>o?.foo : number | undefined +>o : Thing | undefined +>foo : number | undefined +>undefined : undefined + + o; +>o : Thing + + o.foo; +>o.foo : number +>o : Thing +>foo : number + } + if (o?.["foo"] !== undefined) { +>o?.["foo"] !== undefined : boolean +>o?.["foo"] : number | undefined +>o : Thing | undefined +>"foo" : "foo" +>undefined : undefined + + o; +>o : Thing + + o["foo"]; +>o["foo"] : number +>o : Thing +>"foo" : "foo" + } + if (o?.bar() !== undefined) { +>o?.bar() !== undefined : boolean +>o?.bar() : number | undefined +>o?.bar : (() => number) | undefined +>o : Thing | undefined +>bar : (() => number) | undefined +>undefined : undefined + + o; +>o : Thing + + o.bar; +>o.bar : () => number +>o : Thing +>bar : () => number + } +} + +function f14(o: Thing | null) { +>f14 : (o: Thing | null) => void +>o : Thing | null +>null : null + + if (o?.foo !== undefined) { +>o?.foo !== undefined : boolean +>o?.foo : number | undefined +>o : Thing | null +>foo : number | undefined +>undefined : undefined + + o; +>o : Thing + + o.foo; +>o.foo : number +>o : Thing +>foo : number + } + if (o?.["foo"] !== undefined) { +>o?.["foo"] !== undefined : boolean +>o?.["foo"] : number | undefined +>o : Thing | null +>"foo" : "foo" +>undefined : undefined + + o; +>o : Thing + + o["foo"]; +>o["foo"] : number +>o : Thing +>"foo" : "foo" + } + if (o?.bar() !== undefined) { +>o?.bar() !== undefined : boolean +>o?.bar() : number | undefined +>o?.bar : (() => number) | undefined +>o : Thing | null +>bar : (() => number) | undefined +>undefined : undefined + + o; +>o : Thing + + o.bar; +>o.bar : () => number +>o : Thing +>bar : () => number + } +} + From f7b22a0216550db5ce8ab0d11491e3d4f526c00f Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 5 Oct 2019 13:08:24 -0700 Subject: [PATCH 6/8] Support typeof and instanceof --- src/compiler/binder.ts | 5 ++--- src/compiler/checker.ts | 13 +++++++++++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 6bd6045a55101..ee07acdac8299 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -856,9 +856,8 @@ namespace ts { function isNarrowableReference(expr: Expression): boolean { return expr.kind === SyntaxKind.Identifier || expr.kind === SyntaxKind.ThisKeyword || expr.kind === SyntaxKind.SuperKeyword || (isPropertyAccessExpression(expr) || isNonNullExpression(expr) || isParenthesizedExpression(expr)) && isNarrowableReference(expr.expression) || - isElementAccessExpression(expr) && - isStringOrNumericLiteralLike(expr.argumentExpression) && - isNarrowableReference(expr.expression); + isElementAccessExpression(expr) && isStringOrNumericLiteralLike(expr.argumentExpression) && isNarrowableReference(expr.expression) || + isOptionalChain(expr); } function hasNarrowableArgument(expr: CallExpression) { diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5f0f8241a5eca..a759f7179b1a7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -19360,7 +19360,7 @@ namespace ts { if (isMatchingReference(reference, right)) { return narrowTypeByEquality(type, operator, left, assumeTrue); } - if (assumeTrue && (operator === SyntaxKind.EqualsEqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken)) { + if (assumeTrue && strictNullChecks) { if (optionalChainContainsReference(left, reference)) { type = narrowTypeByOptionalChainContainment(type, operator, right); } @@ -19397,7 +19397,9 @@ namespace ts { // the type of obj if (a) the operator is === and the type of value doesn't include undefined or (b) the // operator is !== and the type of value is undefined. const valueType = getTypeOfExpression(value); - return operator === SyntaxKind.EqualsEqualsEqualsToken && !(getTypeFacts(valueType) & TypeFacts.EQUndefined) || + return operator === SyntaxKind.EqualsEqualsToken && !(getTypeFacts(valueType) & TypeFacts.EQUndefinedOrNull) || + operator === SyntaxKind.EqualsEqualsEqualsToken && !(getTypeFacts(valueType) & TypeFacts.EQUndefined) || + operator === SyntaxKind.ExclamationEqualsToken && valueType.flags & TypeFlags.Nullable || operator === SyntaxKind.ExclamationEqualsEqualsToken && valueType.flags & TypeFlags.Undefined ? getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull) : type; } @@ -19452,6 +19454,10 @@ namespace ts { // We have '==', '!=', '===', or !==' operator with 'typeof xxx' and string literal operands const target = getReferenceCandidate(typeOfExpr.expression); if (!isMatchingReference(reference, target)) { + if (assumeTrue && (operator === SyntaxKind.EqualsEqualsToken || operator === SyntaxKind.EqualsEqualsEqualsToken) && + strictNullChecks && optionalChainContainsReference(target, reference)) { + return getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull); + } // For a reference of the form 'x.y', a 'typeof x === ...' type guard resets the // narrowed type of 'y' to its declared type. if (containsMatchingReference(reference, target)) { @@ -19633,6 +19639,9 @@ namespace ts { function narrowTypeByInstanceof(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type { const left = getReferenceCandidate(expr.left); if (!isMatchingReference(reference, left)) { + if (assumeTrue && strictNullChecks && optionalChainContainsReference(left, reference)) { + return getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull); + } // For a reference of the form 'x.y', an 'x instanceof T' type guard resets the // narrowed type of 'y' to its declared type. We do this because preceding 'x.y' // references might reference a different 'y' property. However, we make an exception From f88518187ba5aa6ee0998b18cd78b6136834d1ab Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 5 Oct 2019 13:08:31 -0700 Subject: [PATCH 7/8] Add tests --- .../controlFlow/controlFlowOptionalChain.ts | 125 +++++++++++++++--- 1 file changed, 109 insertions(+), 16 deletions(-) diff --git a/tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts b/tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts index d04fdb9e1b0bc..f54275f309c34 100644 --- a/tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts +++ b/tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts @@ -162,79 +162,172 @@ function f01(x: unknown) { } } -type Thing = { foo: number, bar(): number }; +type Thing = { foo: string | number, bar(): number, baz: object }; function f10(o: Thing | undefined, value: number) { if (o?.foo === value) { - o; o.foo; } if (o?.["foo"] === value) { - o; o["foo"]; } if (o?.bar() === value) { - o; + o.bar; + } + if (o?.foo == value) { + o.foo; + } + if (o?.["foo"] == value) { + o["foo"]; + } + if (o?.bar() == value) { o.bar; } } function f11(o: Thing | null, value: number) { if (o?.foo === value) { - o; o.foo; } if (o?.["foo"] === value) { - o; o["foo"]; } if (o?.bar() === value) { - o; + o.bar; + } + if (o?.foo == value) { + o.foo; + } + if (o?.["foo"] == value) { + o["foo"]; + } + if (o?.bar() == value) { o.bar; } } function f12(o: Thing | undefined, value: number | undefined) { if (o?.foo === value) { - o; o.foo; // Error } if (o?.["foo"] === value) { - o; o["foo"]; // Error } if (o?.bar() === value) { - o; + o.bar; // Error + } + if (o?.foo == value) { + o.foo; // Error + } + if (o?.["foo"] == value) { + o["foo"]; // Error + } + if (o?.bar() == value) { + o.bar; // Error + } +} + +function f12a(o: Thing | undefined, value: number | null) { + if (o?.foo === value) { + o.foo; + } + if (o?.["foo"] === value) { + o["foo"]; + } + if (o?.bar() === value) { + o.bar; + } + if (o?.foo == value) { + o.foo; // Error + } + if (o?.["foo"] == value) { + o["foo"]; // Error + } + if (o?.bar() == value) { o.bar; // Error } } function f13(o: Thing | undefined) { if (o?.foo !== undefined) { - o; o.foo; } if (o?.["foo"] !== undefined) { - o; o["foo"]; } if (o?.bar() !== undefined) { - o; + o.bar; + } + if (o?.foo != undefined) { + o.foo; + } + if (o?.["foo"] != undefined) { + o["foo"]; + } + if (o?.bar() != undefined) { + o.bar; + } +} + +function f13a(o: Thing | undefined) { + if (o?.foo !== null) { + o.foo; // Error + } + if (o?.["foo"] !== null) { + o["foo"]; // Error + } + if (o?.bar() !== null) { + o.bar; // Error + } + if (o?.foo != null) { + o.foo; + } + if (o?.["foo"] != null) { + o["foo"]; + } + if (o?.bar() != null) { o.bar; } } function f14(o: Thing | null) { if (o?.foo !== undefined) { - o; o.foo; } if (o?.["foo"] !== undefined) { - o; o["foo"]; } if (o?.bar() !== undefined) { - o; o.bar; } } + +function f20(o: Thing | undefined) { + if (typeof o?.foo === "number") { + o.foo; + } + if (typeof o?.["foo"] === "number") { + o["foo"]; + } + if (typeof o?.bar() === "number") { + o.bar; + } + if (o?.baz instanceof Error) { + o.baz; + } +} + +function f21(o: Thing | null) { + if (typeof o?.foo === "number") { + o.foo; + } + if (typeof o?.["foo"] === "number") { + o["foo"]; + } + if (typeof o?.bar() === "number") { + o.bar; + } + if (o?.baz instanceof Error) { + o.baz; + } +} From 99cec3a0d6875f842c666f8021228e069beab2d6 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 5 Oct 2019 13:08:39 -0700 Subject: [PATCH 8/8] Accept new baselines --- .../controlFlowOptionalChain.errors.txt | 160 +++++- .../reference/controlFlowOptionalChain.js | 256 +++++++-- .../controlFlowOptionalChain.symbols | 494 +++++++++++++---- .../reference/controlFlowOptionalChain.types | 497 ++++++++++++++++-- 4 files changed, 1194 insertions(+), 213 deletions(-) diff --git a/tests/baselines/reference/controlFlowOptionalChain.errors.txt b/tests/baselines/reference/controlFlowOptionalChain.errors.txt index 1c5c4b18399b5..987dca335dccc 100644 --- a/tests/baselines/reference/controlFlowOptionalChain.errors.txt +++ b/tests/baselines/reference/controlFlowOptionalChain.errors.txt @@ -19,12 +19,21 @@ tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(112,1): error TS tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(130,5): error TS2532: Object is possibly 'undefined'. tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(134,1): error TS2532: Object is possibly 'undefined'. tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(153,9): error TS2775: Assertions require every name in the call target to be declared with an explicit type annotation. -tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(197,9): error TS2532: Object is possibly 'undefined'. -tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(201,9): error TS2532: Object is possibly 'undefined'. -tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(205,9): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(208,9): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(211,9): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(214,9): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(217,9): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(220,9): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(223,9): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(238,9): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(241,9): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(244,9): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(271,9): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(274,9): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(277,9): error TS2532: Object is possibly 'undefined'. -==== tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts (24 errors) ==== +==== tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts (33 errors) ==== // assignments in shortcutting chain declare const o: undefined | { [key: string]: any; @@ -228,53 +237,104 @@ tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(205,9): error TS } } - type Thing = { foo: number, bar(): number }; + type Thing = { foo: string | number, bar(): number, baz: object }; function f10(o: Thing | undefined, value: number) { if (o?.foo === value) { - o; o.foo; } if (o?.["foo"] === value) { - o; o["foo"]; } if (o?.bar() === value) { - o; + o.bar; + } + if (o?.foo == value) { + o.foo; + } + if (o?.["foo"] == value) { + o["foo"]; + } + if (o?.bar() == value) { o.bar; } } function f11(o: Thing | null, value: number) { if (o?.foo === value) { - o; o.foo; } if (o?.["foo"] === value) { - o; o["foo"]; } if (o?.bar() === value) { - o; + o.bar; + } + if (o?.foo == value) { + o.foo; + } + if (o?.["foo"] == value) { + o["foo"]; + } + if (o?.bar() == value) { o.bar; } } function f12(o: Thing | undefined, value: number | undefined) { if (o?.foo === value) { - o; o.foo; // Error ~ !!! error TS2532: Object is possibly 'undefined'. } if (o?.["foo"] === value) { - o; o["foo"]; // Error ~ !!! error TS2532: Object is possibly 'undefined'. } if (o?.bar() === value) { - o; + o.bar; // Error + ~ +!!! error TS2532: Object is possibly 'undefined'. + } + if (o?.foo == value) { + o.foo; // Error + ~ +!!! error TS2532: Object is possibly 'undefined'. + } + if (o?.["foo"] == value) { + o["foo"]; // Error + ~ +!!! error TS2532: Object is possibly 'undefined'. + } + if (o?.bar() == value) { + o.bar; // Error + ~ +!!! error TS2532: Object is possibly 'undefined'. + } + } + + function f12a(o: Thing | undefined, value: number | null) { + if (o?.foo === value) { + o.foo; + } + if (o?.["foo"] === value) { + o["foo"]; + } + if (o?.bar() === value) { + o.bar; + } + if (o?.foo == value) { + o.foo; // Error + ~ +!!! error TS2532: Object is possibly 'undefined'. + } + if (o?.["foo"] == value) { + o["foo"]; // Error + ~ +!!! error TS2532: Object is possibly 'undefined'. + } + if (o?.bar() == value) { o.bar; // Error ~ !!! error TS2532: Object is possibly 'undefined'. @@ -283,31 +343,91 @@ tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(205,9): error TS function f13(o: Thing | undefined) { if (o?.foo !== undefined) { - o; o.foo; } if (o?.["foo"] !== undefined) { - o; o["foo"]; } if (o?.bar() !== undefined) { - o; + o.bar; + } + if (o?.foo != undefined) { + o.foo; + } + if (o?.["foo"] != undefined) { + o["foo"]; + } + if (o?.bar() != undefined) { + o.bar; + } + } + + function f13a(o: Thing | undefined) { + if (o?.foo !== null) { + o.foo; // Error + ~ +!!! error TS2532: Object is possibly 'undefined'. + } + if (o?.["foo"] !== null) { + o["foo"]; // Error + ~ +!!! error TS2532: Object is possibly 'undefined'. + } + if (o?.bar() !== null) { + o.bar; // Error + ~ +!!! error TS2532: Object is possibly 'undefined'. + } + if (o?.foo != null) { + o.foo; + } + if (o?.["foo"] != null) { + o["foo"]; + } + if (o?.bar() != null) { o.bar; } } function f14(o: Thing | null) { if (o?.foo !== undefined) { - o; o.foo; } if (o?.["foo"] !== undefined) { - o; o["foo"]; } if (o?.bar() !== undefined) { - o; o.bar; } } + + function f20(o: Thing | undefined) { + if (typeof o?.foo === "number") { + o.foo; + } + if (typeof o?.["foo"] === "number") { + o["foo"]; + } + if (typeof o?.bar() === "number") { + o.bar; + } + if (o?.baz instanceof Error) { + o.baz; + } + } + + function f21(o: Thing | null) { + if (typeof o?.foo === "number") { + o.foo; + } + if (typeof o?.["foo"] === "number") { + o["foo"]; + } + if (typeof o?.bar() === "number") { + o.bar; + } + if (o?.baz instanceof Error) { + o.baz; + } + } \ No newline at end of file diff --git a/tests/baselines/reference/controlFlowOptionalChain.js b/tests/baselines/reference/controlFlowOptionalChain.js index 9b42d764726bf..214515280350b 100644 --- a/tests/baselines/reference/controlFlowOptionalChain.js +++ b/tests/baselines/reference/controlFlowOptionalChain.js @@ -160,82 +160,175 @@ function f01(x: unknown) { } } -type Thing = { foo: number, bar(): number }; +type Thing = { foo: string | number, bar(): number, baz: object }; function f10(o: Thing | undefined, value: number) { if (o?.foo === value) { - o; o.foo; } if (o?.["foo"] === value) { - o; o["foo"]; } if (o?.bar() === value) { - o; + o.bar; + } + if (o?.foo == value) { + o.foo; + } + if (o?.["foo"] == value) { + o["foo"]; + } + if (o?.bar() == value) { o.bar; } } function f11(o: Thing | null, value: number) { if (o?.foo === value) { - o; o.foo; } if (o?.["foo"] === value) { - o; o["foo"]; } if (o?.bar() === value) { - o; + o.bar; + } + if (o?.foo == value) { + o.foo; + } + if (o?.["foo"] == value) { + o["foo"]; + } + if (o?.bar() == value) { o.bar; } } function f12(o: Thing | undefined, value: number | undefined) { if (o?.foo === value) { - o; o.foo; // Error } if (o?.["foo"] === value) { - o; o["foo"]; // Error } if (o?.bar() === value) { - o; + o.bar; // Error + } + if (o?.foo == value) { + o.foo; // Error + } + if (o?.["foo"] == value) { + o["foo"]; // Error + } + if (o?.bar() == value) { + o.bar; // Error + } +} + +function f12a(o: Thing | undefined, value: number | null) { + if (o?.foo === value) { + o.foo; + } + if (o?.["foo"] === value) { + o["foo"]; + } + if (o?.bar() === value) { + o.bar; + } + if (o?.foo == value) { + o.foo; // Error + } + if (o?.["foo"] == value) { + o["foo"]; // Error + } + if (o?.bar() == value) { o.bar; // Error } } function f13(o: Thing | undefined) { if (o?.foo !== undefined) { - o; o.foo; } if (o?.["foo"] !== undefined) { - o; o["foo"]; } if (o?.bar() !== undefined) { - o; + o.bar; + } + if (o?.foo != undefined) { + o.foo; + } + if (o?.["foo"] != undefined) { + o["foo"]; + } + if (o?.bar() != undefined) { + o.bar; + } +} + +function f13a(o: Thing | undefined) { + if (o?.foo !== null) { + o.foo; // Error + } + if (o?.["foo"] !== null) { + o["foo"]; // Error + } + if (o?.bar() !== null) { + o.bar; // Error + } + if (o?.foo != null) { + o.foo; + } + if (o?.["foo"] != null) { + o["foo"]; + } + if (o?.bar() != null) { o.bar; } } function f14(o: Thing | null) { if (o?.foo !== undefined) { - o; o.foo; } if (o?.["foo"] !== undefined) { - o; o["foo"]; } if (o?.bar() !== undefined) { - o; o.bar; } } + +function f20(o: Thing | undefined) { + if (typeof o?.foo === "number") { + o.foo; + } + if (typeof o?.["foo"] === "number") { + o["foo"]; + } + if (typeof o?.bar() === "number") { + o.bar; + } + if (o?.baz instanceof Error) { + o.baz; + } +} + +function f21(o: Thing | null) { + if (typeof o?.foo === "number") { + o.foo; + } + if (typeof o?.["foo"] === "number") { + o["foo"]; + } + if (typeof o?.bar() === "number") { + o.bar; + } + if (o?.baz instanceof Error) { + o.baz; + } +} //// [controlFlowOptionalChain.js] @@ -364,77 +457,170 @@ function f01(x) { } } function f10(o, value) { - var _a, _b, _c; + var _a, _b, _c, _d, _e, _f; if (((_a = o) === null || _a === void 0 ? void 0 : _a.foo) === value) { - o; o.foo; } if (((_b = o) === null || _b === void 0 ? void 0 : _b["foo"]) === value) { - o; o["foo"]; } if (((_c = o) === null || _c === void 0 ? void 0 : _c.bar()) === value) { - o; + o.bar; + } + if (((_d = o) === null || _d === void 0 ? void 0 : _d.foo) == value) { + o.foo; + } + if (((_e = o) === null || _e === void 0 ? void 0 : _e["foo"]) == value) { + o["foo"]; + } + if (((_f = o) === null || _f === void 0 ? void 0 : _f.bar()) == value) { o.bar; } } function f11(o, value) { - var _a, _b, _c; + var _a, _b, _c, _d, _e, _f; if (((_a = o) === null || _a === void 0 ? void 0 : _a.foo) === value) { - o; o.foo; } if (((_b = o) === null || _b === void 0 ? void 0 : _b["foo"]) === value) { - o; o["foo"]; } if (((_c = o) === null || _c === void 0 ? void 0 : _c.bar()) === value) { - o; + o.bar; + } + if (((_d = o) === null || _d === void 0 ? void 0 : _d.foo) == value) { + o.foo; + } + if (((_e = o) === null || _e === void 0 ? void 0 : _e["foo"]) == value) { + o["foo"]; + } + if (((_f = o) === null || _f === void 0 ? void 0 : _f.bar()) == value) { o.bar; } } function f12(o, value) { - var _a, _b, _c; + var _a, _b, _c, _d, _e, _f; if (((_a = o) === null || _a === void 0 ? void 0 : _a.foo) === value) { - o; o.foo; // Error } if (((_b = o) === null || _b === void 0 ? void 0 : _b["foo"]) === value) { - o; o["foo"]; // Error } if (((_c = o) === null || _c === void 0 ? void 0 : _c.bar()) === value) { - o; + o.bar; // Error + } + if (((_d = o) === null || _d === void 0 ? void 0 : _d.foo) == value) { + o.foo; // Error + } + if (((_e = o) === null || _e === void 0 ? void 0 : _e["foo"]) == value) { + o["foo"]; // Error + } + if (((_f = o) === null || _f === void 0 ? void 0 : _f.bar()) == value) { + o.bar; // Error + } +} +function f12a(o, value) { + var _a, _b, _c, _d, _e, _f; + if (((_a = o) === null || _a === void 0 ? void 0 : _a.foo) === value) { + o.foo; + } + if (((_b = o) === null || _b === void 0 ? void 0 : _b["foo"]) === value) { + o["foo"]; + } + if (((_c = o) === null || _c === void 0 ? void 0 : _c.bar()) === value) { + o.bar; + } + if (((_d = o) === null || _d === void 0 ? void 0 : _d.foo) == value) { + o.foo; // Error + } + if (((_e = o) === null || _e === void 0 ? void 0 : _e["foo"]) == value) { + o["foo"]; // Error + } + if (((_f = o) === null || _f === void 0 ? void 0 : _f.bar()) == value) { o.bar; // Error } } function f13(o) { - var _a, _b, _c; + var _a, _b, _c, _d, _e, _f; if (((_a = o) === null || _a === void 0 ? void 0 : _a.foo) !== undefined) { - o; o.foo; } if (((_b = o) === null || _b === void 0 ? void 0 : _b["foo"]) !== undefined) { - o; o["foo"]; } if (((_c = o) === null || _c === void 0 ? void 0 : _c.bar()) !== undefined) { - o; + o.bar; + } + if (((_d = o) === null || _d === void 0 ? void 0 : _d.foo) != undefined) { + o.foo; + } + if (((_e = o) === null || _e === void 0 ? void 0 : _e["foo"]) != undefined) { + o["foo"]; + } + if (((_f = o) === null || _f === void 0 ? void 0 : _f.bar()) != undefined) { + o.bar; + } +} +function f13a(o) { + var _a, _b, _c, _d, _e, _f; + if (((_a = o) === null || _a === void 0 ? void 0 : _a.foo) !== null) { + o.foo; // Error + } + if (((_b = o) === null || _b === void 0 ? void 0 : _b["foo"]) !== null) { + o["foo"]; // Error + } + if (((_c = o) === null || _c === void 0 ? void 0 : _c.bar()) !== null) { + o.bar; // Error + } + if (((_d = o) === null || _d === void 0 ? void 0 : _d.foo) != null) { + o.foo; + } + if (((_e = o) === null || _e === void 0 ? void 0 : _e["foo"]) != null) { + o["foo"]; + } + if (((_f = o) === null || _f === void 0 ? void 0 : _f.bar()) != null) { o.bar; } } function f14(o) { var _a, _b, _c; if (((_a = o) === null || _a === void 0 ? void 0 : _a.foo) !== undefined) { - o; o.foo; } if (((_b = o) === null || _b === void 0 ? void 0 : _b["foo"]) !== undefined) { - o; o["foo"]; } if (((_c = o) === null || _c === void 0 ? void 0 : _c.bar()) !== undefined) { - o; o.bar; } } +function f20(o) { + var _a, _b, _c, _d; + if (typeof ((_a = o) === null || _a === void 0 ? void 0 : _a.foo) === "number") { + o.foo; + } + if (typeof ((_b = o) === null || _b === void 0 ? void 0 : _b["foo"]) === "number") { + o["foo"]; + } + if (typeof ((_c = o) === null || _c === void 0 ? void 0 : _c.bar()) === "number") { + o.bar; + } + if (((_d = o) === null || _d === void 0 ? void 0 : _d.baz) instanceof Error) { + o.baz; + } +} +function f21(o) { + var _a, _b, _c, _d; + if (typeof ((_a = o) === null || _a === void 0 ? void 0 : _a.foo) === "number") { + o.foo; + } + if (typeof ((_b = o) === null || _b === void 0 ? void 0 : _b["foo"]) === "number") { + o["foo"]; + } + if (typeof ((_c = o) === null || _c === void 0 ? void 0 : _c.bar()) === "number") { + o.bar; + } + if (((_d = o) === null || _d === void 0 ? void 0 : _d.baz) instanceof Error) { + o.baz; + } +} diff --git a/tests/baselines/reference/controlFlowOptionalChain.symbols b/tests/baselines/reference/controlFlowOptionalChain.symbols index 495e6371a6330..2665a960a568a 100644 --- a/tests/baselines/reference/controlFlowOptionalChain.symbols +++ b/tests/baselines/reference/controlFlowOptionalChain.symbols @@ -600,13 +600,14 @@ function f01(x: unknown) { } } -type Thing = { foo: number, bar(): number }; +type Thing = { foo: string | number, bar(): number, baz: object }; >Thing : Symbol(Thing, Decl(controlFlowOptionalChain.ts, 159, 1)) >foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) ->bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 27)) +>bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) +>baz : Symbol(baz, Decl(controlFlowOptionalChain.ts, 161, 51)) function f10(o: Thing | undefined, value: number) { ->f10 : Symbol(f10, Decl(controlFlowOptionalChain.ts, 161, 44)) +>f10 : Symbol(f10, Decl(controlFlowOptionalChain.ts, 161, 66)) >o : Symbol(o, Decl(controlFlowOptionalChain.ts, 163, 13)) >Thing : Symbol(Thing, Decl(controlFlowOptionalChain.ts, 159, 1)) >value : Symbol(value, Decl(controlFlowOptionalChain.ts, 163, 34)) @@ -617,9 +618,6 @@ function f10(o: Thing | undefined, value: number) { >foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) >value : Symbol(value, Decl(controlFlowOptionalChain.ts, 163, 34)) - o; ->o : Symbol(o, Decl(controlFlowOptionalChain.ts, 163, 13)) - o.foo; >o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) >o : Symbol(o, Decl(controlFlowOptionalChain.ts, 163, 13)) @@ -629,211 +627,505 @@ function f10(o: Thing | undefined, value: number) { >o : Symbol(o, Decl(controlFlowOptionalChain.ts, 163, 13)) >value : Symbol(value, Decl(controlFlowOptionalChain.ts, 163, 34)) - o; ->o : Symbol(o, Decl(controlFlowOptionalChain.ts, 163, 13)) - o["foo"]; >o : Symbol(o, Decl(controlFlowOptionalChain.ts, 163, 13)) >"foo" : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) } if (o?.bar() === value) { ->o?.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 27)) +>o?.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 163, 13)) +>bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 163, 34)) + + o.bar; +>o.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 163, 13)) +>bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) + } + if (o?.foo == value) { +>o?.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) >o : Symbol(o, Decl(controlFlowOptionalChain.ts, 163, 13)) ->bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 27)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) >value : Symbol(value, Decl(controlFlowOptionalChain.ts, 163, 34)) - o; + o.foo; +>o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 163, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + if (o?.["foo"] == value) { +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 163, 13)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 163, 34)) + + o["foo"]; +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 163, 13)) +>"foo" : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + if (o?.bar() == value) { +>o?.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) >o : Symbol(o, Decl(controlFlowOptionalChain.ts, 163, 13)) +>bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 163, 34)) o.bar; ->o.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 27)) +>o.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) >o : Symbol(o, Decl(controlFlowOptionalChain.ts, 163, 13)) ->bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 27)) +>bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) } } function f11(o: Thing | null, value: number) { ->f11 : Symbol(f11, Decl(controlFlowOptionalChain.ts, 176, 1)) ->o : Symbol(o, Decl(controlFlowOptionalChain.ts, 178, 13)) +>f11 : Symbol(f11, Decl(controlFlowOptionalChain.ts, 182, 1)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 184, 13)) >Thing : Symbol(Thing, Decl(controlFlowOptionalChain.ts, 159, 1)) ->value : Symbol(value, Decl(controlFlowOptionalChain.ts, 178, 29)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 184, 29)) if (o?.foo === value) { >o?.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) ->o : Symbol(o, Decl(controlFlowOptionalChain.ts, 178, 13)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 184, 13)) >foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) ->value : Symbol(value, Decl(controlFlowOptionalChain.ts, 178, 29)) - - o; ->o : Symbol(o, Decl(controlFlowOptionalChain.ts, 178, 13)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 184, 29)) o.foo; >o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) ->o : Symbol(o, Decl(controlFlowOptionalChain.ts, 178, 13)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 184, 13)) >foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) } if (o?.["foo"] === value) { ->o : Symbol(o, Decl(controlFlowOptionalChain.ts, 178, 13)) ->value : Symbol(value, Decl(controlFlowOptionalChain.ts, 178, 29)) - - o; ->o : Symbol(o, Decl(controlFlowOptionalChain.ts, 178, 13)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 184, 13)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 184, 29)) o["foo"]; ->o : Symbol(o, Decl(controlFlowOptionalChain.ts, 178, 13)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 184, 13)) >"foo" : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) } if (o?.bar() === value) { ->o?.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 27)) ->o : Symbol(o, Decl(controlFlowOptionalChain.ts, 178, 13)) ->bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 27)) ->value : Symbol(value, Decl(controlFlowOptionalChain.ts, 178, 29)) +>o?.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 184, 13)) +>bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 184, 29)) + + o.bar; +>o.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 184, 13)) +>bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) + } + if (o?.foo == value) { +>o?.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 184, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 184, 29)) + + o.foo; +>o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 184, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + if (o?.["foo"] == value) { +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 184, 13)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 184, 29)) - o; ->o : Symbol(o, Decl(controlFlowOptionalChain.ts, 178, 13)) + o["foo"]; +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 184, 13)) +>"foo" : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + if (o?.bar() == value) { +>o?.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 184, 13)) +>bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 184, 29)) o.bar; ->o.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 27)) ->o : Symbol(o, Decl(controlFlowOptionalChain.ts, 178, 13)) ->bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 27)) +>o.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 184, 13)) +>bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) } } function f12(o: Thing | undefined, value: number | undefined) { ->f12 : Symbol(f12, Decl(controlFlowOptionalChain.ts, 191, 1)) ->o : Symbol(o, Decl(controlFlowOptionalChain.ts, 193, 13)) +>f12 : Symbol(f12, Decl(controlFlowOptionalChain.ts, 203, 1)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 205, 13)) >Thing : Symbol(Thing, Decl(controlFlowOptionalChain.ts, 159, 1)) ->value : Symbol(value, Decl(controlFlowOptionalChain.ts, 193, 34)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 205, 34)) if (o?.foo === value) { >o?.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) ->o : Symbol(o, Decl(controlFlowOptionalChain.ts, 193, 13)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 205, 13)) >foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) ->value : Symbol(value, Decl(controlFlowOptionalChain.ts, 193, 34)) - - o; ->o : Symbol(o, Decl(controlFlowOptionalChain.ts, 193, 13)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 205, 34)) o.foo; // Error >o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) ->o : Symbol(o, Decl(controlFlowOptionalChain.ts, 193, 13)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 205, 13)) >foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) } if (o?.["foo"] === value) { ->o : Symbol(o, Decl(controlFlowOptionalChain.ts, 193, 13)) ->value : Symbol(value, Decl(controlFlowOptionalChain.ts, 193, 34)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 205, 13)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 205, 34)) - o; ->o : Symbol(o, Decl(controlFlowOptionalChain.ts, 193, 13)) + o["foo"]; // Error +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 205, 13)) + } + if (o?.bar() === value) { +>o?.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 205, 13)) +>bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 205, 34)) + + o.bar; // Error +>o.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 205, 13)) +>bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) + } + if (o?.foo == value) { +>o?.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 205, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 205, 34)) + + o.foo; // Error +>o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 205, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + if (o?.["foo"] == value) { +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 205, 13)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 205, 34)) o["foo"]; // Error ->o : Symbol(o, Decl(controlFlowOptionalChain.ts, 193, 13)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 205, 13)) + } + if (o?.bar() == value) { +>o?.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 205, 13)) +>bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 205, 34)) + + o.bar; // Error +>o.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 205, 13)) +>bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) + } +} + +function f12a(o: Thing | undefined, value: number | null) { +>f12a : Symbol(f12a, Decl(controlFlowOptionalChain.ts, 224, 1)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 226, 14)) +>Thing : Symbol(Thing, Decl(controlFlowOptionalChain.ts, 159, 1)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 226, 35)) + + if (o?.foo === value) { +>o?.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 226, 14)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 226, 35)) + + o.foo; +>o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 226, 14)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + if (o?.["foo"] === value) { +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 226, 14)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 226, 35)) + + o["foo"]; +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 226, 14)) +>"foo" : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) } if (o?.bar() === value) { ->o?.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 27)) ->o : Symbol(o, Decl(controlFlowOptionalChain.ts, 193, 13)) ->bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 27)) ->value : Symbol(value, Decl(controlFlowOptionalChain.ts, 193, 34)) +>o?.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 226, 14)) +>bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 226, 35)) - o; ->o : Symbol(o, Decl(controlFlowOptionalChain.ts, 193, 13)) + o.bar; +>o.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 226, 14)) +>bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) + } + if (o?.foo == value) { +>o?.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 226, 14)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 226, 35)) + + o.foo; // Error +>o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 226, 14)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + if (o?.["foo"] == value) { +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 226, 14)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 226, 35)) + + o["foo"]; // Error +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 226, 14)) + } + if (o?.bar() == value) { +>o?.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 226, 14)) +>bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 226, 35)) o.bar; // Error ->o.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 27)) ->o : Symbol(o, Decl(controlFlowOptionalChain.ts, 193, 13)) ->bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 27)) +>o.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 226, 14)) +>bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) } } function f13(o: Thing | undefined) { ->f13 : Symbol(f13, Decl(controlFlowOptionalChain.ts, 206, 1)) ->o : Symbol(o, Decl(controlFlowOptionalChain.ts, 208, 13)) +>f13 : Symbol(f13, Decl(controlFlowOptionalChain.ts, 245, 1)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 247, 13)) >Thing : Symbol(Thing, Decl(controlFlowOptionalChain.ts, 159, 1)) if (o?.foo !== undefined) { >o?.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) ->o : Symbol(o, Decl(controlFlowOptionalChain.ts, 208, 13)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 247, 13)) >foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) >undefined : Symbol(undefined) - o; ->o : Symbol(o, Decl(controlFlowOptionalChain.ts, 208, 13)) - o.foo; >o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) ->o : Symbol(o, Decl(controlFlowOptionalChain.ts, 208, 13)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 247, 13)) >foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) } if (o?.["foo"] !== undefined) { ->o : Symbol(o, Decl(controlFlowOptionalChain.ts, 208, 13)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 247, 13)) >undefined : Symbol(undefined) - o; ->o : Symbol(o, Decl(controlFlowOptionalChain.ts, 208, 13)) - o["foo"]; ->o : Symbol(o, Decl(controlFlowOptionalChain.ts, 208, 13)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 247, 13)) >"foo" : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) } if (o?.bar() !== undefined) { ->o?.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 27)) ->o : Symbol(o, Decl(controlFlowOptionalChain.ts, 208, 13)) ->bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 27)) +>o?.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 247, 13)) +>bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) +>undefined : Symbol(undefined) + + o.bar; +>o.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 247, 13)) +>bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) + } + if (o?.foo != undefined) { +>o?.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 247, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>undefined : Symbol(undefined) + + o.foo; +>o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 247, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + if (o?.["foo"] != undefined) { +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 247, 13)) >undefined : Symbol(undefined) - o; ->o : Symbol(o, Decl(controlFlowOptionalChain.ts, 208, 13)) + o["foo"]; +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 247, 13)) +>"foo" : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + if (o?.bar() != undefined) { +>o?.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 247, 13)) +>bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) +>undefined : Symbol(undefined) o.bar; ->o.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 27)) ->o : Symbol(o, Decl(controlFlowOptionalChain.ts, 208, 13)) ->bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 27)) +>o.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 247, 13)) +>bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) + } +} + +function f13a(o: Thing | undefined) { +>f13a : Symbol(f13a, Decl(controlFlowOptionalChain.ts, 266, 1)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 268, 14)) +>Thing : Symbol(Thing, Decl(controlFlowOptionalChain.ts, 159, 1)) + + if (o?.foo !== null) { +>o?.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 268, 14)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + + o.foo; // Error +>o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 268, 14)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + if (o?.["foo"] !== null) { +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 268, 14)) + + o["foo"]; // Error +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 268, 14)) + } + if (o?.bar() !== null) { +>o?.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 268, 14)) +>bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) + + o.bar; // Error +>o.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 268, 14)) +>bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) + } + if (o?.foo != null) { +>o?.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 268, 14)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + + o.foo; +>o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 268, 14)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + if (o?.["foo"] != null) { +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 268, 14)) + + o["foo"]; +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 268, 14)) +>"foo" : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + if (o?.bar() != null) { +>o?.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 268, 14)) +>bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) + + o.bar; +>o.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 268, 14)) +>bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) } } function f14(o: Thing | null) { ->f14 : Symbol(f14, Decl(controlFlowOptionalChain.ts, 221, 1)) ->o : Symbol(o, Decl(controlFlowOptionalChain.ts, 223, 13)) +>f14 : Symbol(f14, Decl(controlFlowOptionalChain.ts, 287, 1)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 289, 13)) >Thing : Symbol(Thing, Decl(controlFlowOptionalChain.ts, 159, 1)) if (o?.foo !== undefined) { >o?.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) ->o : Symbol(o, Decl(controlFlowOptionalChain.ts, 223, 13)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 289, 13)) >foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) >undefined : Symbol(undefined) - o; ->o : Symbol(o, Decl(controlFlowOptionalChain.ts, 223, 13)) - o.foo; >o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) ->o : Symbol(o, Decl(controlFlowOptionalChain.ts, 223, 13)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 289, 13)) >foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) } if (o?.["foo"] !== undefined) { ->o : Symbol(o, Decl(controlFlowOptionalChain.ts, 223, 13)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 289, 13)) >undefined : Symbol(undefined) - o; ->o : Symbol(o, Decl(controlFlowOptionalChain.ts, 223, 13)) - o["foo"]; ->o : Symbol(o, Decl(controlFlowOptionalChain.ts, 223, 13)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 289, 13)) >"foo" : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) } if (o?.bar() !== undefined) { ->o?.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 27)) ->o : Symbol(o, Decl(controlFlowOptionalChain.ts, 223, 13)) ->bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 27)) +>o?.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 289, 13)) +>bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) >undefined : Symbol(undefined) - o; ->o : Symbol(o, Decl(controlFlowOptionalChain.ts, 223, 13)) + o.bar; +>o.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 289, 13)) +>bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) + } +} + +function f20(o: Thing | undefined) { +>f20 : Symbol(f20, Decl(controlFlowOptionalChain.ts, 299, 1)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 301, 13)) +>Thing : Symbol(Thing, Decl(controlFlowOptionalChain.ts, 159, 1)) + + if (typeof o?.foo === "number") { +>o?.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 301, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + + o.foo; +>o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 301, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + if (typeof o?.["foo"] === "number") { +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 301, 13)) + + o["foo"]; +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 301, 13)) +>"foo" : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + if (typeof o?.bar() === "number") { +>o?.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 301, 13)) +>bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) o.bar; ->o.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 27)) ->o : Symbol(o, Decl(controlFlowOptionalChain.ts, 223, 13)) ->bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 27)) +>o.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 301, 13)) +>bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) + } + if (o?.baz instanceof Error) { +>o?.baz : Symbol(baz, Decl(controlFlowOptionalChain.ts, 161, 51)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 301, 13)) +>baz : Symbol(baz, Decl(controlFlowOptionalChain.ts, 161, 51)) +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + + o.baz; +>o.baz : Symbol(baz, Decl(controlFlowOptionalChain.ts, 161, 51)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 301, 13)) +>baz : Symbol(baz, Decl(controlFlowOptionalChain.ts, 161, 51)) + } +} + +function f21(o: Thing | null) { +>f21 : Symbol(f21, Decl(controlFlowOptionalChain.ts, 314, 1)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 316, 13)) +>Thing : Symbol(Thing, Decl(controlFlowOptionalChain.ts, 159, 1)) + + if (typeof o?.foo === "number") { +>o?.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 316, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + + o.foo; +>o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 316, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + if (typeof o?.["foo"] === "number") { +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 316, 13)) + + o["foo"]; +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 316, 13)) +>"foo" : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + if (typeof o?.bar() === "number") { +>o?.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 316, 13)) +>bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) + + o.bar; +>o.bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 316, 13)) +>bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) + } + if (o?.baz instanceof Error) { +>o?.baz : Symbol(baz, Decl(controlFlowOptionalChain.ts, 161, 51)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 316, 13)) +>baz : Symbol(baz, Decl(controlFlowOptionalChain.ts, 161, 51)) +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + + o.baz; +>o.baz : Symbol(baz, Decl(controlFlowOptionalChain.ts, 161, 51)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 316, 13)) +>baz : Symbol(baz, Decl(controlFlowOptionalChain.ts, 161, 51)) } } diff --git a/tests/baselines/reference/controlFlowOptionalChain.types b/tests/baselines/reference/controlFlowOptionalChain.types index 0c243dc6be68b..ffec7d1525e4e 100644 --- a/tests/baselines/reference/controlFlowOptionalChain.types +++ b/tests/baselines/reference/controlFlowOptionalChain.types @@ -634,10 +634,11 @@ function f01(x: unknown) { } } -type Thing = { foo: number, bar(): number }; +type Thing = { foo: string | number, bar(): number, baz: object }; >Thing : Thing ->foo : number +>foo : string | number >bar : () => number +>baz : object function f10(o: Thing | undefined, value: number) { >f10 : (o: Thing | undefined, value: number) => void @@ -646,14 +647,11 @@ function f10(o: Thing | undefined, value: number) { if (o?.foo === value) { >o?.foo === value : boolean ->o?.foo : number | undefined +>o?.foo : string | number | undefined >o : Thing | undefined ->foo : number | undefined +>foo : string | number | undefined >value : number - o; ->o : Thing - o.foo; >o.foo : number >o : Thing @@ -661,14 +659,11 @@ function f10(o: Thing | undefined, value: number) { } if (o?.["foo"] === value) { >o?.["foo"] === value : boolean ->o?.["foo"] : number | undefined +>o?.["foo"] : string | number | undefined >o : Thing | undefined >"foo" : "foo" >value : number - o; ->o : Thing - o["foo"]; >o["foo"] : number >o : Thing @@ -682,8 +677,42 @@ function f10(o: Thing | undefined, value: number) { >bar : (() => number) | undefined >value : number - o; + o.bar; +>o.bar : () => number +>o : Thing +>bar : () => number + } + if (o?.foo == value) { +>o?.foo == value : boolean +>o?.foo : string | number | undefined +>o : Thing | undefined +>foo : string | number | undefined +>value : number + + o.foo; +>o.foo : string | number +>o : Thing +>foo : string | number + } + if (o?.["foo"] == value) { +>o?.["foo"] == value : boolean +>o?.["foo"] : string | number | undefined +>o : Thing | undefined +>"foo" : "foo" +>value : number + + o["foo"]; +>o["foo"] : string | number >o : Thing +>"foo" : "foo" + } + if (o?.bar() == value) { +>o?.bar() == value : boolean +>o?.bar() : number | undefined +>o?.bar : (() => number) | undefined +>o : Thing | undefined +>bar : (() => number) | undefined +>value : number o.bar; >o.bar : () => number @@ -700,14 +729,11 @@ function f11(o: Thing | null, value: number) { if (o?.foo === value) { >o?.foo === value : boolean ->o?.foo : number | undefined +>o?.foo : string | number | undefined >o : Thing | null ->foo : number | undefined +>foo : string | number | undefined >value : number - o; ->o : Thing - o.foo; >o.foo : number >o : Thing @@ -715,14 +741,11 @@ function f11(o: Thing | null, value: number) { } if (o?.["foo"] === value) { >o?.["foo"] === value : boolean ->o?.["foo"] : number | undefined +>o?.["foo"] : string | number | undefined >o : Thing | null >"foo" : "foo" >value : number - o; ->o : Thing - o["foo"]; >o["foo"] : number >o : Thing @@ -736,8 +759,42 @@ function f11(o: Thing | null, value: number) { >bar : (() => number) | undefined >value : number - o; + o.bar; +>o.bar : () => number >o : Thing +>bar : () => number + } + if (o?.foo == value) { +>o?.foo == value : boolean +>o?.foo : string | number | undefined +>o : Thing | null +>foo : string | number | undefined +>value : number + + o.foo; +>o.foo : string | number +>o : Thing +>foo : string | number + } + if (o?.["foo"] == value) { +>o?.["foo"] == value : boolean +>o?.["foo"] : string | number | undefined +>o : Thing | null +>"foo" : "foo" +>value : number + + o["foo"]; +>o["foo"] : string | number +>o : Thing +>"foo" : "foo" + } + if (o?.bar() == value) { +>o?.bar() == value : boolean +>o?.bar() : number | undefined +>o?.bar : (() => number) | undefined +>o : Thing | null +>bar : (() => number) | undefined +>value : number o.bar; >o.bar : () => number @@ -753,14 +810,11 @@ function f12(o: Thing | undefined, value: number | undefined) { if (o?.foo === value) { >o?.foo === value : boolean ->o?.foo : number | undefined +>o?.foo : string | number | undefined >o : Thing | undefined ->foo : number | undefined +>foo : string | number | undefined >value : number | undefined - o; ->o : Thing | undefined - o.foo; // Error >o.foo : number >o : Thing | undefined @@ -768,17 +822,96 @@ function f12(o: Thing | undefined, value: number | undefined) { } if (o?.["foo"] === value) { >o?.["foo"] === value : boolean ->o?.["foo"] : number | undefined +>o?.["foo"] : string | number | undefined >o : Thing | undefined >"foo" : "foo" >value : number | undefined - o; + o["foo"]; // Error +>o["foo"] : number +>o : Thing | undefined +>"foo" : "foo" + } + if (o?.bar() === value) { +>o?.bar() === value : boolean +>o?.bar() : number | undefined +>o?.bar : (() => number) | undefined +>o : Thing | undefined +>bar : (() => number) | undefined +>value : number | undefined + + o.bar; // Error +>o.bar : () => number +>o : Thing | undefined +>bar : () => number + } + if (o?.foo == value) { +>o?.foo == value : boolean +>o?.foo : string | number | undefined +>o : Thing | undefined +>foo : string | number | undefined +>value : number | undefined + + o.foo; // Error +>o.foo : number >o : Thing | undefined +>foo : number + } + if (o?.["foo"] == value) { +>o?.["foo"] == value : boolean +>o?.["foo"] : string | number | undefined +>o : Thing | undefined +>"foo" : "foo" +>value : number | undefined o["foo"]; // Error >o["foo"] : number >o : Thing | undefined +>"foo" : "foo" + } + if (o?.bar() == value) { +>o?.bar() == value : boolean +>o?.bar() : number | undefined +>o?.bar : (() => number) | undefined +>o : Thing | undefined +>bar : (() => number) | undefined +>value : number | undefined + + o.bar; // Error +>o.bar : () => number +>o : Thing | undefined +>bar : () => number + } +} + +function f12a(o: Thing | undefined, value: number | null) { +>f12a : (o: Thing | undefined, value: number | null) => void +>o : Thing | undefined +>value : number | null +>null : null + + if (o?.foo === value) { +>o?.foo === value : boolean +>o?.foo : string | number | undefined +>o : Thing | undefined +>foo : string | number | undefined +>value : number | null + + o.foo; +>o.foo : number +>o : Thing +>foo : number + } + if (o?.["foo"] === value) { +>o?.["foo"] === value : boolean +>o?.["foo"] : string | number | undefined +>o : Thing | undefined +>"foo" : "foo" +>value : number | null + + o["foo"]; +>o["foo"] : number +>o : Thing >"foo" : "foo" } if (o?.bar() === value) { @@ -787,10 +920,44 @@ function f12(o: Thing | undefined, value: number | undefined) { >o?.bar : (() => number) | undefined >o : Thing | undefined >bar : (() => number) | undefined ->value : number | undefined +>value : number | null - o; + o.bar; +>o.bar : () => number +>o : Thing +>bar : () => number + } + if (o?.foo == value) { +>o?.foo == value : boolean +>o?.foo : string | number | undefined >o : Thing | undefined +>foo : string | number | undefined +>value : number | null + + o.foo; // Error +>o.foo : number +>o : Thing | undefined +>foo : number + } + if (o?.["foo"] == value) { +>o?.["foo"] == value : boolean +>o?.["foo"] : string | number | undefined +>o : Thing | undefined +>"foo" : "foo" +>value : number | null + + o["foo"]; // Error +>o["foo"] : number +>o : Thing | undefined +>"foo" : "foo" + } + if (o?.bar() == value) { +>o?.bar() == value : boolean +>o?.bar() : number | undefined +>o?.bar : (() => number) | undefined +>o : Thing | undefined +>bar : (() => number) | undefined +>value : number | null o.bar; // Error >o.bar : () => number @@ -805,31 +972,25 @@ function f13(o: Thing | undefined) { if (o?.foo !== undefined) { >o?.foo !== undefined : boolean ->o?.foo : number | undefined +>o?.foo : string | number | undefined >o : Thing | undefined ->foo : number | undefined +>foo : string | number | undefined >undefined : undefined - o; ->o : Thing - o.foo; ->o.foo : number +>o.foo : string | number >o : Thing ->foo : number +>foo : string | number } if (o?.["foo"] !== undefined) { >o?.["foo"] !== undefined : boolean ->o?.["foo"] : number | undefined +>o?.["foo"] : string | number | undefined >o : Thing | undefined >"foo" : "foo" >undefined : undefined - o; ->o : Thing - o["foo"]; ->o["foo"] : number +>o["foo"] : string | number >o : Thing >"foo" : "foo" } @@ -841,8 +1002,122 @@ function f13(o: Thing | undefined) { >bar : (() => number) | undefined >undefined : undefined - o; + o.bar; +>o.bar : () => number +>o : Thing +>bar : () => number + } + if (o?.foo != undefined) { +>o?.foo != undefined : boolean +>o?.foo : string | number | undefined +>o : Thing | undefined +>foo : string | number | undefined +>undefined : undefined + + o.foo; +>o.foo : string | number +>o : Thing +>foo : string | number + } + if (o?.["foo"] != undefined) { +>o?.["foo"] != undefined : boolean +>o?.["foo"] : string | number | undefined +>o : Thing | undefined +>"foo" : "foo" +>undefined : undefined + + o["foo"]; +>o["foo"] : string | number +>o : Thing +>"foo" : "foo" + } + if (o?.bar() != undefined) { +>o?.bar() != undefined : boolean +>o?.bar() : number | undefined +>o?.bar : (() => number) | undefined +>o : Thing | undefined +>bar : (() => number) | undefined +>undefined : undefined + + o.bar; +>o.bar : () => number >o : Thing +>bar : () => number + } +} + +function f13a(o: Thing | undefined) { +>f13a : (o: Thing | undefined) => void +>o : Thing | undefined + + if (o?.foo !== null) { +>o?.foo !== null : boolean +>o?.foo : string | number | undefined +>o : Thing | undefined +>foo : string | number | undefined +>null : null + + o.foo; // Error +>o.foo : string | number +>o : Thing | undefined +>foo : string | number + } + if (o?.["foo"] !== null) { +>o?.["foo"] !== null : boolean +>o?.["foo"] : string | number | undefined +>o : Thing | undefined +>"foo" : "foo" +>null : null + + o["foo"]; // Error +>o["foo"] : string | number +>o : Thing | undefined +>"foo" : "foo" + } + if (o?.bar() !== null) { +>o?.bar() !== null : boolean +>o?.bar() : number | undefined +>o?.bar : (() => number) | undefined +>o : Thing | undefined +>bar : (() => number) | undefined +>null : null + + o.bar; // Error +>o.bar : () => number +>o : Thing | undefined +>bar : () => number + } + if (o?.foo != null) { +>o?.foo != null : boolean +>o?.foo : string | number | undefined +>o : Thing | undefined +>foo : string | number | undefined +>null : null + + o.foo; +>o.foo : string | number +>o : Thing +>foo : string | number + } + if (o?.["foo"] != null) { +>o?.["foo"] != null : boolean +>o?.["foo"] : string | number | undefined +>o : Thing | undefined +>"foo" : "foo" +>null : null + + o["foo"]; +>o["foo"] : string | number +>o : Thing +>"foo" : "foo" + } + if (o?.bar() != null) { +>o?.bar() != null : boolean +>o?.bar() : number | undefined +>o?.bar : (() => number) | undefined +>o : Thing | undefined +>bar : (() => number) | undefined +>null : null o.bar; >o.bar : () => number @@ -858,31 +1133,25 @@ function f14(o: Thing | null) { if (o?.foo !== undefined) { >o?.foo !== undefined : boolean ->o?.foo : number | undefined +>o?.foo : string | number | undefined >o : Thing | null ->foo : number | undefined +>foo : string | number | undefined >undefined : undefined - o; ->o : Thing - o.foo; ->o.foo : number +>o.foo : string | number >o : Thing ->foo : number +>foo : string | number } if (o?.["foo"] !== undefined) { >o?.["foo"] !== undefined : boolean ->o?.["foo"] : number | undefined +>o?.["foo"] : string | number | undefined >o : Thing | null >"foo" : "foo" >undefined : undefined - o; ->o : Thing - o["foo"]; ->o["foo"] : number +>o["foo"] : string | number >o : Thing >"foo" : "foo" } @@ -894,13 +1163,127 @@ function f14(o: Thing | null) { >bar : (() => number) | undefined >undefined : undefined - o; + o.bar; +>o.bar : () => number +>o : Thing +>bar : () => number + } +} + +function f20(o: Thing | undefined) { +>f20 : (o: Thing | undefined) => void +>o : Thing | undefined + + if (typeof o?.foo === "number") { +>typeof o?.foo === "number" : boolean +>typeof o?.foo : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>o?.foo : string | number | undefined +>o : Thing | undefined +>foo : string | number | undefined +>"number" : "number" + + o.foo; +>o.foo : number >o : Thing +>foo : number + } + if (typeof o?.["foo"] === "number") { +>typeof o?.["foo"] === "number" : boolean +>typeof o?.["foo"] : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>o?.["foo"] : string | number | undefined +>o : Thing | undefined +>"foo" : "foo" +>"number" : "number" + + o["foo"]; +>o["foo"] : number +>o : Thing +>"foo" : "foo" + } + if (typeof o?.bar() === "number") { +>typeof o?.bar() === "number" : boolean +>typeof o?.bar() : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>o?.bar() : number | undefined +>o?.bar : (() => number) | undefined +>o : Thing | undefined +>bar : (() => number) | undefined +>"number" : "number" o.bar; >o.bar : () => number >o : Thing >bar : () => number } + if (o?.baz instanceof Error) { +>o?.baz instanceof Error : boolean +>o?.baz : object | undefined +>o : Thing | undefined +>baz : object | undefined +>Error : ErrorConstructor + + o.baz; +>o.baz : Error +>o : Thing +>baz : Error + } +} + +function f21(o: Thing | null) { +>f21 : (o: Thing | null) => void +>o : Thing | null +>null : null + + if (typeof o?.foo === "number") { +>typeof o?.foo === "number" : boolean +>typeof o?.foo : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>o?.foo : string | number | undefined +>o : Thing | null +>foo : string | number | undefined +>"number" : "number" + + o.foo; +>o.foo : number +>o : Thing +>foo : number + } + if (typeof o?.["foo"] === "number") { +>typeof o?.["foo"] === "number" : boolean +>typeof o?.["foo"] : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>o?.["foo"] : string | number | undefined +>o : Thing | null +>"foo" : "foo" +>"number" : "number" + + o["foo"]; +>o["foo"] : number +>o : Thing +>"foo" : "foo" + } + if (typeof o?.bar() === "number") { +>typeof o?.bar() === "number" : boolean +>typeof o?.bar() : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>o?.bar() : number | undefined +>o?.bar : (() => number) | undefined +>o : Thing | null +>bar : (() => number) | undefined +>"number" : "number" + + o.bar; +>o.bar : () => number +>o : Thing +>bar : () => number + } + if (o?.baz instanceof Error) { +>o?.baz instanceof Error : boolean +>o?.baz : object | undefined +>o : Thing | null +>baz : object | undefined +>Error : ErrorConstructor + + o.baz; +>o.baz : Error +>o : Thing +>baz : Error + } }