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 6ece23f022ab3..a759f7179b1a7 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 && strictNullChecks) { + 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,18 @@ 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.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; + } + function narrowTypeByEquality(type: Type, operator: SyntaxKind, value: Expression, assumeTrue: boolean): Type { if (type.flags & TypeFlags.Any) { return type; @@ -19424,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)) { @@ -19605,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 diff --git a/tests/baselines/reference/controlFlowOptionalChain.errors.txt b/tests/baselines/reference/controlFlowOptionalChain.errors.txt index ce46b232b3d81..987dca335dccc 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'. @@ -20,9 +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(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 (22 errors) ==== +==== tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts (33 errors) ==== // assignments in shortcutting chain declare const o: undefined | { [key: string]: any; @@ -99,10 +110,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 { @@ -227,4 +236,198 @@ tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(153,9): error TS x; } } + + type Thing = { foo: string | number, bar(): number, baz: object }; + + function f10(o: Thing | undefined, value: number) { + if (o?.foo === value) { + o.foo; + } + if (o?.["foo"] === value) { + o["foo"]; + } + if (o?.bar() === value) { + 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.foo; + } + if (o?.["foo"] === value) { + o["foo"]; + } + if (o?.bar() === value) { + 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.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'. + } + 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'. + } + } + + function f13(o: Thing | undefined) { + if (o?.foo !== undefined) { + o.foo; + } + if (o?.["foo"] !== undefined) { + o["foo"]; + } + if (o?.bar() !== undefined) { + 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.foo; + } + if (o?.["foo"] !== undefined) { + o["foo"]; + } + if (o?.bar() !== undefined) { + 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 58a2873f93aa1..214515280350b 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 { @@ -159,6 +159,176 @@ function f01(x: unknown) { x; } } + +type Thing = { foo: string | number, bar(): number, baz: object }; + +function f10(o: Thing | undefined, value: number) { + if (o?.foo === value) { + o.foo; + } + if (o?.["foo"] === value) { + o["foo"]; + } + if (o?.bar() === value) { + 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.foo; + } + if (o?.["foo"] === value) { + o["foo"]; + } + if (o?.bar() === value) { + 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.foo; // Error + } + if (o?.["foo"] === value) { + o["foo"]; // Error + } + if (o?.bar() === value) { + 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.foo; + } + if (o?.["foo"] !== undefined) { + o["foo"]; + } + if (o?.bar() !== undefined) { + 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.foo; + } + if (o?.["foo"] !== undefined) { + o["foo"]; + } + if (o?.bar() !== undefined) { + 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] @@ -206,8 +376,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 { @@ -286,3 +456,171 @@ function f01(x) { x; } } +function f10(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; + } + 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, _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; + } + 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, _d, _e, _f; + if (((_a = o) === null || _a === void 0 ? void 0 : _a.foo) === value) { + o.foo; // Error + } + if (((_b = o) === null || _b === void 0 ? void 0 : _b["foo"]) === value) { + o["foo"]; // Error + } + if (((_c = o) === null || _c === void 0 ? void 0 : _c.bar()) === value) { + 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, _d, _e, _f; + if (((_a = o) === null || _a === void 0 ? void 0 : _a.foo) !== undefined) { + o.foo; + } + if (((_b = o) === null || _b === void 0 ? void 0 : _b["foo"]) !== undefined) { + o["foo"]; + } + if (((_c = o) === null || _c === void 0 ? void 0 : _c.bar()) !== undefined) { + 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.foo; + } + if (((_b = o) === null || _b === void 0 ? void 0 : _b["foo"]) !== undefined) { + o["foo"]; + } + if (((_c = o) === null || _c === void 0 ? void 0 : _c.bar()) !== undefined) { + 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 21f1de2a41026..2665a960a568a 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; @@ -600,3 +600,532 @@ function f01(x: unknown) { } } +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, 36)) +>baz : Symbol(baz, Decl(controlFlowOptionalChain.ts, 161, 51)) + +function f10(o: Thing | undefined, value: number) { +>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)) + + 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.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, 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)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 163, 34)) + + 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, 36)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 163, 13)) +>bar : Symbol(bar, Decl(controlFlowOptionalChain.ts, 161, 36)) + } +} + +function f11(o: Thing | null, value: number) { +>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, 184, 29)) + + 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["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, 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["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, 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, 203, 1)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 205, 13)) +>Thing : Symbol(Thing, Decl(controlFlowOptionalChain.ts, 159, 1)) +>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, 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, 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, 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, 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; +>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, 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, 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, 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["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, 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["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, 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, 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, 289, 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, 289, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + if (o?.["foo"] !== undefined) { +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 289, 13)) +>undefined : Symbol(undefined) + + o["foo"]; +>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, 36)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 289, 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, 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, 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 a93c5b0b43f04..ffec7d1525e4e 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; @@ -634,3 +634,656 @@ function f01(x: unknown) { } } +type Thing = { foo: string | number, bar(): number, baz: object }; +>Thing : Thing +>foo : string | number +>bar : () => number +>baz : object + +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 : string | number | undefined +>o : Thing | undefined +>foo : string | number | undefined +>value : number + + 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 + + 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.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 +>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 : string | number | undefined +>o : Thing | null +>foo : string | number | undefined +>value : number + + o.foo; +>o.foo : number +>o : Thing +>foo : 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"] : 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 +>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 +>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 : 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 + } + 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) { +>o?.bar() === value : boolean +>o?.bar() : number | undefined +>o?.bar : (() => number) | undefined +>o : Thing | undefined +>bar : (() => number) | undefined +>value : number | null + + 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 +>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 : 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 + } + 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 +>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 : string | number | undefined +>o : Thing | null +>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 | null +>"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 | null +>bar : (() => number) | undefined +>undefined : undefined + + 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 + } +} + diff --git a/tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts b/tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts index dadb1b36076db..f54275f309c34 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 { @@ -161,3 +161,173 @@ function f01(x: unknown) { x; } } + +type Thing = { foo: string | number, bar(): number, baz: object }; + +function f10(o: Thing | undefined, value: number) { + if (o?.foo === value) { + o.foo; + } + if (o?.["foo"] === value) { + o["foo"]; + } + if (o?.bar() === value) { + 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.foo; + } + if (o?.["foo"] === value) { + o["foo"]; + } + if (o?.bar() === value) { + 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.foo; // Error + } + if (o?.["foo"] === value) { + o["foo"]; // Error + } + if (o?.bar() === value) { + 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.foo; + } + if (o?.["foo"] !== undefined) { + o["foo"]; + } + if (o?.bar() !== undefined) { + 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.foo; + } + if (o?.["foo"] !== undefined) { + o["foo"]; + } + if (o?.bar() !== undefined) { + 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; + } +}