diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 96a783a6c9e73..7c6336276f90a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -18787,6 +18787,14 @@ namespace ts { signature.declaration && (getReturnTypeFromAnnotation(signature.declaration) || unknownType).flags & TypeFlags.Never); } + function getTypePredicateArgument(predicate: TypePredicate, callExpression: CallExpression) { + if (predicate.kind === TypePredicateKind.Identifier || predicate.kind === TypePredicateKind.AssertsIdentifier) { + return callExpression.arguments[predicate.parameterIndex]; + } + const invokedExpression = skipParentheses(callExpression.expression); + return isAccessExpression(invokedExpression) ? skipParentheses(invokedExpression.expression) : undefined; + } + function reportFlowControlError(node: Node) { const block = findAncestor(node, isFunctionOrModuleBlock); const sourceFile = getSourceFileOfNode(node); @@ -19175,20 +19183,30 @@ namespace ts { if (isMatchingReference(reference, expr)) { type = narrowTypeBySwitchOnDiscriminant(type, flow.switchStatement, flow.clauseStart, flow.clauseEnd); } - else if (isMatchingReferenceDiscriminant(expr, type)) { - type = narrowTypeByDiscriminant( - type, - expr as AccessExpression, - t => narrowTypeBySwitchOnDiscriminant(t, flow.switchStatement, flow.clauseStart, flow.clauseEnd)); - } else if (expr.kind === SyntaxKind.TypeOfExpression && isMatchingReference(reference, (expr as TypeOfExpression).expression)) { type = narrowBySwitchOnTypeOf(type, flow.switchStatement, flow.clauseStart, flow.clauseEnd); } - else if (containsMatchingReferenceDiscriminant(reference, expr)) { - type = declaredType; - } - else if (flow.clauseStart === flow.clauseEnd && isExhaustiveSwitchStatement(flow.switchStatement)) { - return unreachableNeverType; + else { + if (strictNullChecks) { + if (optionalChainContainsReference(expr, reference)) { + type = narrowTypeBySwitchOptionalChainContainment(type, flow.switchStatement, flow.clauseStart, flow.clauseEnd, + t => !(t.flags & (TypeFlags.Undefined | TypeFlags.Never))); + } + else if (expr.kind === SyntaxKind.TypeOfExpression && optionalChainContainsReference((expr as TypeOfExpression).expression, reference)) { + type = narrowTypeBySwitchOptionalChainContainment(type, flow.switchStatement, flow.clauseStart, flow.clauseEnd, + t => !(t.flags & TypeFlags.Never || t.flags & TypeFlags.StringLiteral && (t).value === "undefined")); + } + } + if (isMatchingReferenceDiscriminant(expr, type)) { + type = narrowTypeByDiscriminant(type, expr as AccessExpression, + t => narrowTypeBySwitchOnDiscriminant(t, flow.switchStatement, flow.clauseStart, flow.clauseEnd)); + } + else if (containsMatchingReferenceDiscriminant(reference, expr)) { + type = declaredType; + } + else if (flow.clauseStart === flow.clauseEnd && isExhaustiveSwitchStatement(flow.switchStatement)) { + return unreachableNeverType; + } } return createFlowType(type, isIncomplete(flowType)); } @@ -19333,6 +19351,9 @@ namespace ts { if (isMatchingReference(reference, expr)) { return getTypeWithFacts(type, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy); } + if (strictNullChecks && assumeTrue && optionalChainContainsReference(expr, reference)) { + type = getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull); + } if (isMatchingReferenceDiscriminant(expr, declaredType)) { return narrowTypeByDiscriminant(type, expr, t => getTypeWithFacts(t, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy)); } @@ -19384,12 +19405,12 @@ namespace ts { if (isMatchingReference(reference, right)) { return narrowTypeByEquality(type, operator, left, assumeTrue); } - if (assumeTrue && strictNullChecks) { + if (strictNullChecks) { if (optionalChainContainsReference(left, reference)) { - type = narrowTypeByOptionalChainContainment(type, operator, right); + type = narrowTypeByOptionalChainContainment(type, operator, right, assumeTrue); } else if (optionalChainContainsReference(right, reference)) { - type = narrowTypeByOptionalChainContainment(type, operator, left); + type = narrowTypeByOptionalChainContainment(type, operator, left, assumeTrue); } } if (isMatchingReferenceDiscriminant(left, declaredType)) { @@ -19416,16 +19437,14 @@ 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 + function narrowTypeByOptionalChainContainment(type: Type, operator: SyntaxKind, value: Expression, assumeTrue: boolean): Type { + // We are in a 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; + const effectiveTrue = operator === SyntaxKind.EqualsEqualsToken || operator === SyntaxKind.EqualsEqualsEqualsToken ? assumeTrue : !assumeTrue; + const doubleEquals = operator === SyntaxKind.EqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsToken; + const valueNonNullish = !(getTypeFacts(getTypeOfExpression(value)) & (doubleEquals ? TypeFacts.EQUndefinedOrNull : TypeFacts.EQUndefined)); + return effectiveTrue === valueNonNullish ? getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull) : type; } function narrowTypeByEquality(type: Type, operator: SyntaxKind, value: Expression, assumeTrue: boolean): Type { @@ -19476,10 +19495,12 @@ namespace ts { function narrowTypeByTypeof(type: Type, typeOfExpr: TypeOfExpression, operator: SyntaxKind, literal: LiteralExpression, assumeTrue: boolean): Type { // We have '==', '!=', '===', or !==' operator with 'typeof xxx' and string literal operands + if (operator === SyntaxKind.ExclamationEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken) { + assumeTrue = !assumeTrue; + } const target = getReferenceCandidate(typeOfExpr.expression); if (!isMatchingReference(reference, target)) { - if (assumeTrue && (operator === SyntaxKind.EqualsEqualsToken || operator === SyntaxKind.EqualsEqualsEqualsToken) && - strictNullChecks && optionalChainContainsReference(target, reference)) { + if (strictNullChecks && optionalChainContainsReference(target, reference) && assumeTrue === (literal.text !== "undefined")) { return getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull); } // For a reference of the form 'x.y', a 'typeof x === ...' type guard resets the @@ -19489,9 +19510,6 @@ namespace ts { } return type; } - if (operator === SyntaxKind.ExclamationEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken) { - assumeTrue = !assumeTrue; - } if (type.flags & TypeFlags.Any && literal.text === "function") { return type; } @@ -19526,6 +19544,11 @@ namespace ts { } } + function narrowTypeBySwitchOptionalChainContainment(type: Type, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number, clauseCheck: (type: Type) => boolean) { + const everyClauseChecks = clauseStart !== clauseEnd && every(getSwitchClauseTypes(switchStatement).slice(clauseStart, clauseEnd), clauseCheck); + return everyClauseChecks ? getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull) : type; + } + function narrowTypeBySwitchOnDiscriminant(type: Type, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number) { // We only narrow if all case expressions specify // values with unit types, except for the case where @@ -19746,28 +19769,17 @@ namespace ts { function narrowTypeByTypePredicate(type: Type, predicate: TypePredicate, callExpression: CallExpression, assumeTrue: boolean): Type { // Don't narrow from 'any' if the predicate type is exactly 'Object' or 'Function' - if (isTypeAny(type) && (predicate.type === globalObjectType || predicate.type === globalFunctionType)) { - return type; - } - if (predicate.kind === TypePredicateKind.Identifier || predicate.kind === TypePredicateKind.AssertsIdentifier) { - const predicateArgument = callExpression.arguments[predicate.parameterIndex]; - if (predicateArgument && predicate.type) { + if (predicate.type && !(isTypeAny(type) && (predicate.type === globalObjectType || predicate.type === globalFunctionType))) { + const predicateArgument = getTypePredicateArgument(predicate, callExpression); + if (predicateArgument) { if (isMatchingReference(reference, predicateArgument)) { return getNarrowedType(type, predicate.type, assumeTrue, isTypeSubtypeOf); } - if (containsMatchingReference(reference, predicateArgument)) { - return declaredType; - } - } - } - else { - const invokedExpression = skipParentheses(callExpression.expression); - if (isAccessExpression(invokedExpression) && predicate.type) { - const possibleReference = skipParentheses(invokedExpression.expression); - if (isMatchingReference(reference, possibleReference)) { - return getNarrowedType(type, predicate.type, assumeTrue, isTypeSubtypeOf); + if (strictNullChecks && assumeTrue && optionalChainContainsReference(predicateArgument, reference) && + !(getTypeFacts(predicate.type) & TypeFacts.EQUndefined)) { + return getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull); } - if (containsMatchingReference(reference, possibleReference)) { + if (containsMatchingReference(reference, predicateArgument)) { return declaredType; } } diff --git a/tests/baselines/reference/controlFlowOptionalChain.errors.txt b/tests/baselines/reference/controlFlowOptionalChain.errors.txt index 987dca335dccc..ee77494d1c828 100644 --- a/tests/baselines/reference/controlFlowOptionalChain.errors.txt +++ b/tests/baselines/reference/controlFlowOptionalChain.errors.txt @@ -31,9 +31,29 @@ tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(244,9): error TS 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(307,9): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(310,9): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(319,9): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(322,9): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(331,9): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(340,9): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(343,9): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(352,9): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(391,9): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(394,9): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(403,9): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(406,9): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(415,9): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(424,9): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(427,9): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(436,9): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(471,13): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(474,13): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(488,13): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(491,13): error TS2532: Object is possibly 'undefined'. -==== tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts (33 errors) ==== +==== tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts (53 errors) ==== // assignments in shortcutting chain declare const o: undefined | { [key: string]: any; @@ -401,6 +421,76 @@ tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(277,9): error TS } } + function f15(o: Thing | undefined, value: number) { + if (o?.foo === value) { + o.foo; + } + else { + o.foo; // Error + ~ +!!! error TS2532: Object is possibly 'undefined'. + } + if (o?.foo !== value) { + o.foo; // Error + ~ +!!! error TS2532: Object is possibly 'undefined'. + } + else { + o.foo; + } + if (o?.foo == value) { + o.foo; + } + else { + o.foo; // Error + ~ +!!! error TS2532: Object is possibly 'undefined'. + } + if (o?.foo != value) { + o.foo; // Error + ~ +!!! error TS2532: Object is possibly 'undefined'. + } + else { + o.foo; + } + } + + function f16(o: Thing | undefined) { + if (o?.foo === undefined) { + o.foo; // Error + ~ +!!! error TS2532: Object is possibly 'undefined'. + } + else { + o.foo; + } + if (o?.foo !== undefined) { + o.foo; + } + else { + o.foo; // Error + ~ +!!! error TS2532: Object is possibly 'undefined'. + } + if (o?.foo == undefined) { + o.foo; // Error + ~ +!!! error TS2532: Object is possibly 'undefined'. + } + else { + o.foo; + } + if (o?.foo != undefined) { + o.foo; + } + else { + o.foo; // Error + ~ +!!! error TS2532: Object is possibly 'undefined'. + } + } + function f20(o: Thing | undefined) { if (typeof o?.foo === "number") { o.foo; @@ -430,4 +520,171 @@ tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(277,9): error TS o.baz; } } + + function f22(o: Thing | undefined) { + if (typeof o?.foo === "number") { + o.foo; + } + else { + o.foo; // Error + ~ +!!! error TS2532: Object is possibly 'undefined'. + } + if (typeof o?.foo !== "number") { + o.foo; // Error + ~ +!!! error TS2532: Object is possibly 'undefined'. + } + else { + o.foo; + } + if (typeof o?.foo == "number") { + o.foo; + } + else { + o.foo; // Error + ~ +!!! error TS2532: Object is possibly 'undefined'. + } + if (typeof o?.foo != "number") { + o.foo; // Error + ~ +!!! error TS2532: Object is possibly 'undefined'. + } + else { + o.foo; + } + } + + function f23(o: Thing | undefined) { + if (typeof o?.foo === "undefined") { + o.foo; // Error + ~ +!!! error TS2532: Object is possibly 'undefined'. + } + else { + o.foo; + } + if (typeof o?.foo !== "undefined") { + o.foo; + } + else { + o.foo; // Error + ~ +!!! error TS2532: Object is possibly 'undefined'. + } + if (typeof o?.foo == "undefined") { + o.foo; // Error + ~ +!!! error TS2532: Object is possibly 'undefined'. + } + else { + o.foo; + } + if (typeof o?.foo != "undefined") { + o.foo; + } + else { + o.foo; // Error + ~ +!!! error TS2532: Object is possibly 'undefined'. + } + } + + declare function assert(x: unknown): asserts x; + declare function assertNonNull(x: T): asserts x is NonNullable; + + function f30(o: Thing | undefined) { + if (!!true) { + assert(o?.foo); + o.foo; + } + if (!!true) { + assert(o?.foo === 42); + o.foo; + } + if (!!true) { + assert(typeof o?.foo === "number"); + o.foo; + } + if (!!true) { + assertNonNull(o?.foo); + o.foo; + } + } + + function f40(o: Thing | undefined) { + switch (o?.foo) { + case "abc": + o.foo; + break; + case 42: + o.foo; + break; + case undefined: + o.foo; // Error + ~ +!!! error TS2532: Object is possibly 'undefined'. + break; + default: + o.foo; // Error + ~ +!!! error TS2532: Object is possibly 'undefined'. + break; + } + } + + function f41(o: Thing | undefined) { + switch (typeof o?.foo) { + case "string": + o.foo; + break; + case "number": + o.foo; + break; + case "undefined": + o.foo; // Error + ~ +!!! error TS2532: Object is possibly 'undefined'. + break; + default: + o.foo; // Error + ~ +!!! error TS2532: Object is possibly 'undefined'. + break; + } + } + + // Repros from #34570 + + type Shape = + | { type: 'rectangle', width: number, height: number } + | { type: 'circle', radius: number } + + function getArea(shape?: Shape) { + switch (shape?.type) { + case 'circle': + return Math.PI * shape.radius ** 2 + case 'rectangle': + return shape.width * shape.height + default: + return 0 + } + } + + type Feature = { + id: string; + geometry?: { + type: string; + coordinates: number[]; + }; + }; + + + function extractCoordinates(f: Feature): number[] { + if (f.geometry?.type !== 'test') { + return []; + } + return f.geometry.coordinates; + } \ No newline at end of file diff --git a/tests/baselines/reference/controlFlowOptionalChain.js b/tests/baselines/reference/controlFlowOptionalChain.js index 214515280350b..3bb2bab1c0dd3 100644 --- a/tests/baselines/reference/controlFlowOptionalChain.js +++ b/tests/baselines/reference/controlFlowOptionalChain.js @@ -300,6 +300,60 @@ function f14(o: Thing | null) { } } +function f15(o: Thing | undefined, value: number) { + if (o?.foo === value) { + o.foo; + } + else { + o.foo; // Error + } + if (o?.foo !== value) { + o.foo; // Error + } + else { + o.foo; + } + if (o?.foo == value) { + o.foo; + } + else { + o.foo; // Error + } + if (o?.foo != value) { + o.foo; // Error + } + else { + o.foo; + } +} + +function f16(o: Thing | undefined) { + if (o?.foo === undefined) { + o.foo; // Error + } + else { + o.foo; + } + if (o?.foo !== undefined) { + o.foo; + } + else { + o.foo; // Error + } + if (o?.foo == undefined) { + o.foo; // Error + } + else { + o.foo; + } + if (o?.foo != undefined) { + o.foo; + } + else { + o.foo; // Error + } +} + function f20(o: Thing | undefined) { if (typeof o?.foo === "number") { o.foo; @@ -329,6 +383,149 @@ function f21(o: Thing | null) { o.baz; } } + +function f22(o: Thing | undefined) { + if (typeof o?.foo === "number") { + o.foo; + } + else { + o.foo; // Error + } + if (typeof o?.foo !== "number") { + o.foo; // Error + } + else { + o.foo; + } + if (typeof o?.foo == "number") { + o.foo; + } + else { + o.foo; // Error + } + if (typeof o?.foo != "number") { + o.foo; // Error + } + else { + o.foo; + } +} + +function f23(o: Thing | undefined) { + if (typeof o?.foo === "undefined") { + o.foo; // Error + } + else { + o.foo; + } + if (typeof o?.foo !== "undefined") { + o.foo; + } + else { + o.foo; // Error + } + if (typeof o?.foo == "undefined") { + o.foo; // Error + } + else { + o.foo; + } + if (typeof o?.foo != "undefined") { + o.foo; + } + else { + o.foo; // Error + } +} + +declare function assert(x: unknown): asserts x; +declare function assertNonNull(x: T): asserts x is NonNullable; + +function f30(o: Thing | undefined) { + if (!!true) { + assert(o?.foo); + o.foo; + } + if (!!true) { + assert(o?.foo === 42); + o.foo; + } + if (!!true) { + assert(typeof o?.foo === "number"); + o.foo; + } + if (!!true) { + assertNonNull(o?.foo); + o.foo; + } +} + +function f40(o: Thing | undefined) { + switch (o?.foo) { + case "abc": + o.foo; + break; + case 42: + o.foo; + break; + case undefined: + o.foo; // Error + break; + default: + o.foo; // Error + break; + } +} + +function f41(o: Thing | undefined) { + switch (typeof o?.foo) { + case "string": + o.foo; + break; + case "number": + o.foo; + break; + case "undefined": + o.foo; // Error + break; + default: + o.foo; // Error + break; + } +} + +// Repros from #34570 + +type Shape = + | { type: 'rectangle', width: number, height: number } + | { type: 'circle', radius: number } + +function getArea(shape?: Shape) { + switch (shape?.type) { + case 'circle': + return Math.PI * shape.radius ** 2 + case 'rectangle': + return shape.width * shape.height + default: + return 0 + } +} + +type Feature = { + id: string; + geometry?: { + type: string; + coordinates: number[]; + }; +}; + + +function extractCoordinates(f: Feature): number[] { + if (f.geometry?.type !== 'test') { + return []; + } + return f.geometry.coordinates; +} //// [controlFlowOptionalChain.js] @@ -594,6 +791,60 @@ function f14(o) { o.bar; } } +function f15(o, value) { + var _a, _b, _c, _d; + if (((_a = o) === null || _a === void 0 ? void 0 : _a.foo) === value) { + o.foo; + } + else { + o.foo; // Error + } + if (((_b = o) === null || _b === void 0 ? void 0 : _b.foo) !== value) { + o.foo; // Error + } + else { + o.foo; + } + if (((_c = o) === null || _c === void 0 ? void 0 : _c.foo) == value) { + o.foo; + } + else { + o.foo; // Error + } + if (((_d = o) === null || _d === void 0 ? void 0 : _d.foo) != value) { + o.foo; // Error + } + else { + o.foo; + } +} +function f16(o) { + var _a, _b, _c, _d; + if (((_a = o) === null || _a === void 0 ? void 0 : _a.foo) === undefined) { + o.foo; // Error + } + else { + o.foo; + } + if (((_b = o) === null || _b === void 0 ? void 0 : _b.foo) !== undefined) { + o.foo; + } + else { + o.foo; // Error + } + if (((_c = o) === null || _c === void 0 ? void 0 : _c.foo) == undefined) { + o.foo; // Error + } + else { + o.foo; + } + if (((_d = o) === null || _d === void 0 ? void 0 : _d.foo) != undefined) { + o.foo; + } + else { + o.foo; // Error + } +} function f20(o) { var _a, _b, _c, _d; if (typeof ((_a = o) === null || _a === void 0 ? void 0 : _a.foo) === "number") { @@ -624,3 +875,128 @@ function f21(o) { o.baz; } } +function f22(o) { + var _a, _b, _c, _d; + if (typeof ((_a = o) === null || _a === void 0 ? void 0 : _a.foo) === "number") { + o.foo; + } + else { + o.foo; // Error + } + if (typeof ((_b = o) === null || _b === void 0 ? void 0 : _b.foo) !== "number") { + o.foo; // Error + } + else { + o.foo; + } + if (typeof ((_c = o) === null || _c === void 0 ? void 0 : _c.foo) == "number") { + o.foo; + } + else { + o.foo; // Error + } + if (typeof ((_d = o) === null || _d === void 0 ? void 0 : _d.foo) != "number") { + o.foo; // Error + } + else { + o.foo; + } +} +function f23(o) { + var _a, _b, _c, _d; + if (typeof ((_a = o) === null || _a === void 0 ? void 0 : _a.foo) === "undefined") { + o.foo; // Error + } + else { + o.foo; + } + if (typeof ((_b = o) === null || _b === void 0 ? void 0 : _b.foo) !== "undefined") { + o.foo; + } + else { + o.foo; // Error + } + if (typeof ((_c = o) === null || _c === void 0 ? void 0 : _c.foo) == "undefined") { + o.foo; // Error + } + else { + o.foo; + } + if (typeof ((_d = o) === null || _d === void 0 ? void 0 : _d.foo) != "undefined") { + o.foo; + } + else { + o.foo; // Error + } +} +function f30(o) { + var _a, _b, _c, _d; + if (!!true) { + assert((_a = o) === null || _a === void 0 ? void 0 : _a.foo); + o.foo; + } + if (!!true) { + assert(((_b = o) === null || _b === void 0 ? void 0 : _b.foo) === 42); + o.foo; + } + if (!!true) { + assert(typeof ((_c = o) === null || _c === void 0 ? void 0 : _c.foo) === "number"); + o.foo; + } + if (!!true) { + assertNonNull((_d = o) === null || _d === void 0 ? void 0 : _d.foo); + o.foo; + } +} +function f40(o) { + var _a; + switch ((_a = o) === null || _a === void 0 ? void 0 : _a.foo) { + case "abc": + o.foo; + break; + case 42: + o.foo; + break; + case undefined: + o.foo; // Error + break; + default: + o.foo; // Error + break; + } +} +function f41(o) { + var _a; + switch (typeof ((_a = o) === null || _a === void 0 ? void 0 : _a.foo)) { + case "string": + o.foo; + break; + case "number": + o.foo; + break; + case "undefined": + o.foo; // Error + break; + default: + o.foo; // Error + break; + } +} +function getArea(shape) { + var _a; + switch ((_a = shape) === null || _a === void 0 ? void 0 : _a.type) { + case 'circle': + return Math.PI * Math.pow(shape.radius, 2); + case 'rectangle': + return shape.width * shape.height; + default: + return 0; + } +} +function extractCoordinates(f) { + var _a; + if (((_a = f.geometry) === null || _a === void 0 ? void 0 : _a.type) !== 'test') { + return []; + } + return f.geometry.coordinates; +} diff --git a/tests/baselines/reference/controlFlowOptionalChain.symbols b/tests/baselines/reference/controlFlowOptionalChain.symbols index 2665a960a568a..cac0899cc8910 100644 --- a/tests/baselines/reference/controlFlowOptionalChain.symbols +++ b/tests/baselines/reference/controlFlowOptionalChain.symbols @@ -1039,93 +1039,627 @@ function f14(o: Thing | null) { } } -function f20(o: Thing | undefined) { ->f20 : Symbol(f20, Decl(controlFlowOptionalChain.ts, 299, 1)) +function f15(o: Thing | undefined, value: number) { +>f15 : Symbol(f15, Decl(controlFlowOptionalChain.ts, 299, 1)) >o : Symbol(o, Decl(controlFlowOptionalChain.ts, 301, 13)) >Thing : Symbol(Thing, Decl(controlFlowOptionalChain.ts, 159, 1)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 301, 34)) - if (typeof o?.foo === "number") { + if (o?.foo === value) { >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)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 301, 34)) 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") { + else { + o.foo; // Error +>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 (o?.foo !== value) { +>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)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 301, 34)) - o["foo"]; + o.foo; // Error +>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)) + } + else { + 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 (o?.foo == value) { +>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)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 301, 34)) + + 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)) + } + else { + o.foo; // Error +>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 (o?.foo != value) { +>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)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 301, 34)) + + o.foo; // Error +>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)) + } + else { + 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)) + } +} + +function f16(o: Thing | undefined) { +>f16 : Symbol(f16, Decl(controlFlowOptionalChain.ts, 326, 1)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 328, 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, 328, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>undefined : Symbol(undefined) + + o.foo; // Error +>o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 328, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + else { + o.foo; +>o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 328, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + if (o?.foo !== undefined) { +>o?.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 328, 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, 328, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + else { + o.foo; // Error +>o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 328, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + if (o?.foo == undefined) { +>o?.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 328, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>undefined : Symbol(undefined) + + o.foo; // Error +>o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 328, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + else { + o.foo; +>o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 328, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + if (o?.foo != undefined) { +>o?.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 328, 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, 328, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + else { + o.foo; // Error +>o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 328, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } +} + +function f20(o: Thing | undefined) { +>f20 : Symbol(f20, Decl(controlFlowOptionalChain.ts, 353, 1)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 355, 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, 355, 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, 355, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + if (typeof o?.["foo"] === "number") { +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 355, 13)) + + o["foo"]; +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 355, 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)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 355, 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)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 355, 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)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 355, 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)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 355, 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)) +>f21 : Symbol(f21, Decl(controlFlowOptionalChain.ts, 368, 1)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 370, 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)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 370, 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)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 370, 13)) >foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) } if (typeof o?.["foo"] === "number") { ->o : Symbol(o, Decl(controlFlowOptionalChain.ts, 316, 13)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 370, 13)) o["foo"]; ->o : Symbol(o, Decl(controlFlowOptionalChain.ts, 316, 13)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 370, 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)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 370, 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)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 370, 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)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 370, 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)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 370, 13)) >baz : Symbol(baz, Decl(controlFlowOptionalChain.ts, 161, 51)) } } +function f22(o: Thing | undefined) { +>f22 : Symbol(f22, Decl(controlFlowOptionalChain.ts, 383, 1)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 385, 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, 385, 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, 385, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + else { + o.foo; // Error +>o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 385, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + if (typeof o?.foo !== "number") { +>o?.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 385, 13)) +>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, 385, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + else { + o.foo; +>o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 385, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + if (typeof o?.foo == "number") { +>o?.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 385, 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, 385, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + else { + o.foo; // Error +>o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 385, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + if (typeof o?.foo != "number") { +>o?.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 385, 13)) +>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, 385, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + else { + o.foo; +>o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 385, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } +} + +function f23(o: Thing | undefined) { +>f23 : Symbol(f23, Decl(controlFlowOptionalChain.ts, 410, 1)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 412, 13)) +>Thing : Symbol(Thing, Decl(controlFlowOptionalChain.ts, 159, 1)) + + if (typeof o?.foo === "undefined") { +>o?.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 412, 13)) +>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, 412, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + else { + o.foo; +>o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 412, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + if (typeof o?.foo !== "undefined") { +>o?.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 412, 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, 412, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + else { + o.foo; // Error +>o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 412, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + if (typeof o?.foo == "undefined") { +>o?.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 412, 13)) +>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, 412, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + else { + o.foo; +>o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 412, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + if (typeof o?.foo != "undefined") { +>o?.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 412, 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, 412, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + else { + o.foo; // Error +>o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 412, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } +} + +declare function assert(x: unknown): asserts x; +>assert : Symbol(assert, Decl(controlFlowOptionalChain.ts, 437, 1)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 439, 24)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 439, 24)) + +declare function assertNonNull(x: T): asserts x is NonNullable; +>assertNonNull : Symbol(assertNonNull, Decl(controlFlowOptionalChain.ts, 439, 47)) +>T : Symbol(T, Decl(controlFlowOptionalChain.ts, 440, 31)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 440, 34)) +>T : Symbol(T, Decl(controlFlowOptionalChain.ts, 440, 31)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 440, 34)) +>NonNullable : Symbol(NonNullable, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(controlFlowOptionalChain.ts, 440, 31)) + +function f30(o: Thing | undefined) { +>f30 : Symbol(f30, Decl(controlFlowOptionalChain.ts, 440, 69)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 442, 13)) +>Thing : Symbol(Thing, Decl(controlFlowOptionalChain.ts, 159, 1)) + + if (!!true) { + assert(o?.foo); +>assert : Symbol(assert, Decl(controlFlowOptionalChain.ts, 437, 1)) +>o?.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 442, 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, 442, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + if (!!true) { + assert(o?.foo === 42); +>assert : Symbol(assert, Decl(controlFlowOptionalChain.ts, 437, 1)) +>o?.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 442, 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, 442, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + if (!!true) { + assert(typeof o?.foo === "number"); +>assert : Symbol(assert, Decl(controlFlowOptionalChain.ts, 437, 1)) +>o?.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 442, 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, 442, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } + if (!!true) { + assertNonNull(o?.foo); +>assertNonNull : Symbol(assertNonNull, Decl(controlFlowOptionalChain.ts, 439, 47)) +>o?.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 442, 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, 442, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + } +} + +function f40(o: Thing | undefined) { +>f40 : Symbol(f40, Decl(controlFlowOptionalChain.ts, 459, 1)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 461, 13)) +>Thing : Symbol(Thing, Decl(controlFlowOptionalChain.ts, 159, 1)) + + switch (o?.foo) { +>o?.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 461, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + + case "abc": + o.foo; +>o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 461, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + + break; + case 42: + o.foo; +>o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 461, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + + break; + case undefined: +>undefined : Symbol(undefined) + + o.foo; // Error +>o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 461, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + + break; + default: + o.foo; // Error +>o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 461, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + + break; + } +} + +function f41(o: Thing | undefined) { +>f41 : Symbol(f41, Decl(controlFlowOptionalChain.ts, 476, 1)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 478, 13)) +>Thing : Symbol(Thing, Decl(controlFlowOptionalChain.ts, 159, 1)) + + switch (typeof o?.foo) { +>o?.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 478, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + + case "string": + o.foo; +>o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 478, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + + break; + case "number": + o.foo; +>o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 478, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + + break; + case "undefined": + o.foo; // Error +>o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 478, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + + break; + default: + o.foo; // Error +>o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 478, 13)) +>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14)) + + break; + } +} + +// Repros from #34570 + +type Shape = +>Shape : Symbol(Shape, Decl(controlFlowOptionalChain.ts, 493, 1)) + + | { type: 'rectangle', width: number, height: number } +>type : Symbol(type, Decl(controlFlowOptionalChain.ts, 498, 7)) +>width : Symbol(width, Decl(controlFlowOptionalChain.ts, 498, 26)) +>height : Symbol(height, Decl(controlFlowOptionalChain.ts, 498, 41)) + + | { type: 'circle', radius: number } +>type : Symbol(type, Decl(controlFlowOptionalChain.ts, 499, 7)) +>radius : Symbol(radius, Decl(controlFlowOptionalChain.ts, 499, 23)) + +function getArea(shape?: Shape) { +>getArea : Symbol(getArea, Decl(controlFlowOptionalChain.ts, 499, 40)) +>shape : Symbol(shape, Decl(controlFlowOptionalChain.ts, 501, 17)) +>Shape : Symbol(Shape, Decl(controlFlowOptionalChain.ts, 493, 1)) + + switch (shape?.type) { +>shape?.type : Symbol(type, Decl(controlFlowOptionalChain.ts, 498, 7), Decl(controlFlowOptionalChain.ts, 499, 7)) +>shape : Symbol(shape, Decl(controlFlowOptionalChain.ts, 501, 17)) +>type : Symbol(type, Decl(controlFlowOptionalChain.ts, 498, 7), Decl(controlFlowOptionalChain.ts, 499, 7)) + + case 'circle': + return Math.PI * shape.radius ** 2 +>Math.PI : Symbol(Math.PI, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>PI : Symbol(Math.PI, Decl(lib.es5.d.ts, --, --)) +>shape.radius : Symbol(radius, Decl(controlFlowOptionalChain.ts, 499, 23)) +>shape : Symbol(shape, Decl(controlFlowOptionalChain.ts, 501, 17)) +>radius : Symbol(radius, Decl(controlFlowOptionalChain.ts, 499, 23)) + + case 'rectangle': + return shape.width * shape.height +>shape.width : Symbol(width, Decl(controlFlowOptionalChain.ts, 498, 26)) +>shape : Symbol(shape, Decl(controlFlowOptionalChain.ts, 501, 17)) +>width : Symbol(width, Decl(controlFlowOptionalChain.ts, 498, 26)) +>shape.height : Symbol(height, Decl(controlFlowOptionalChain.ts, 498, 41)) +>shape : Symbol(shape, Decl(controlFlowOptionalChain.ts, 501, 17)) +>height : Symbol(height, Decl(controlFlowOptionalChain.ts, 498, 41)) + + default: + return 0 + } +} + +type Feature = { +>Feature : Symbol(Feature, Decl(controlFlowOptionalChain.ts, 510, 1)) + + id: string; +>id : Symbol(id, Decl(controlFlowOptionalChain.ts, 512, 16)) + + geometry?: { +>geometry : Symbol(geometry, Decl(controlFlowOptionalChain.ts, 513, 13)) + + type: string; +>type : Symbol(type, Decl(controlFlowOptionalChain.ts, 514, 14)) + + coordinates: number[]; +>coordinates : Symbol(coordinates, Decl(controlFlowOptionalChain.ts, 515, 17)) + + }; +}; + + +function extractCoordinates(f: Feature): number[] { +>extractCoordinates : Symbol(extractCoordinates, Decl(controlFlowOptionalChain.ts, 518, 2)) +>f : Symbol(f, Decl(controlFlowOptionalChain.ts, 521, 28)) +>Feature : Symbol(Feature, Decl(controlFlowOptionalChain.ts, 510, 1)) + + if (f.geometry?.type !== 'test') { +>f.geometry?.type : Symbol(type, Decl(controlFlowOptionalChain.ts, 514, 14)) +>f.geometry : Symbol(geometry, Decl(controlFlowOptionalChain.ts, 513, 13)) +>f : Symbol(f, Decl(controlFlowOptionalChain.ts, 521, 28)) +>geometry : Symbol(geometry, Decl(controlFlowOptionalChain.ts, 513, 13)) +>type : Symbol(type, Decl(controlFlowOptionalChain.ts, 514, 14)) + + return []; + } + return f.geometry.coordinates; +>f.geometry.coordinates : Symbol(coordinates, Decl(controlFlowOptionalChain.ts, 515, 17)) +>f.geometry : Symbol(geometry, Decl(controlFlowOptionalChain.ts, 513, 13)) +>f : Symbol(f, Decl(controlFlowOptionalChain.ts, 521, 28)) +>geometry : Symbol(geometry, Decl(controlFlowOptionalChain.ts, 513, 13)) +>coordinates : Symbol(coordinates, Decl(controlFlowOptionalChain.ts, 515, 17)) +} + diff --git a/tests/baselines/reference/controlFlowOptionalChain.types b/tests/baselines/reference/controlFlowOptionalChain.types index ffec7d1525e4e..13dd6cd5fd0b1 100644 --- a/tests/baselines/reference/controlFlowOptionalChain.types +++ b/tests/baselines/reference/controlFlowOptionalChain.types @@ -1170,6 +1170,163 @@ function f14(o: Thing | null) { } } +function f15(o: Thing | undefined, value: number) { +>f15 : (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 + } + else { + o.foo; // Error +>o.foo : string | number +>o : Thing | undefined +>foo : string | 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; // Error +>o.foo : string | number +>o : Thing | undefined +>foo : string | number + } + else { + 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 : string | number | undefined +>value : number + + o.foo; +>o.foo : string | number +>o : Thing +>foo : string | number + } + else { + o.foo; // Error +>o.foo : string | number +>o : Thing | undefined +>foo : string | 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; // Error +>o.foo : string | number +>o : Thing | undefined +>foo : string | number + } + else { + o.foo; +>o.foo : number +>o : Thing +>foo : number + } +} + +function f16(o: Thing | undefined) { +>f16 : (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; // Error +>o.foo : never +>o : Thing | undefined +>foo : never + } + else { + 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 : string | number | undefined +>undefined : undefined + + o.foo; +>o.foo : string | number +>o : Thing +>foo : string | number + } + else { + o.foo; // Error +>o.foo : never +>o : Thing | undefined +>foo : never + } + if (o?.foo == undefined) { +>o?.foo == undefined : boolean +>o?.foo : string | number | undefined +>o : Thing | undefined +>foo : string | number | undefined +>undefined : undefined + + o.foo; // Error +>o.foo : never +>o : Thing | undefined +>foo : never + } + else { + 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 : string | number | undefined +>undefined : undefined + + o.foo; +>o.foo : string | number +>o : Thing +>foo : string | number + } + else { + o.foo; // Error +>o.foo : never +>o : Thing | undefined +>foo : never + } +} + function f20(o: Thing | undefined) { >f20 : (o: Thing | undefined) => void >o : Thing | undefined @@ -1287,3 +1444,445 @@ function f21(o: Thing | null) { } } +function f22(o: Thing | undefined) { +>f22 : (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 + } + else { + o.foo; // Error +>o.foo : string +>o : Thing | undefined +>foo : string + } + 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; // Error +>o.foo : string +>o : Thing | undefined +>foo : string + } + else { + 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 : string | number | undefined +>"number" : "number" + + o.foo; +>o.foo : number +>o : Thing +>foo : number + } + else { + o.foo; // Error +>o.foo : string +>o : Thing | undefined +>foo : string + } + 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; // Error +>o.foo : string +>o : Thing | undefined +>foo : string + } + else { + o.foo; +>o.foo : number +>o : Thing +>foo : number + } +} + +function f23(o: Thing | undefined) { +>f23 : (o: Thing | undefined) => void +>o : Thing | undefined + + if (typeof o?.foo === "undefined") { +>typeof o?.foo === "undefined" : boolean +>typeof o?.foo : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>o?.foo : string | number | undefined +>o : Thing | undefined +>foo : string | number | undefined +>"undefined" : "undefined" + + o.foo; // Error +>o.foo : never +>o : Thing | undefined +>foo : never + } + else { + o.foo; +>o.foo : string | number +>o : Thing +>foo : string | number + } + if (typeof o?.foo !== "undefined") { +>typeof o?.foo !== "undefined" : boolean +>typeof o?.foo : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>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 + } + else { + o.foo; // Error +>o.foo : never +>o : Thing | undefined +>foo : never + } + if (typeof o?.foo == "undefined") { +>typeof o?.foo == "undefined" : boolean +>typeof o?.foo : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>o?.foo : string | number | undefined +>o : Thing | undefined +>foo : string | number | undefined +>"undefined" : "undefined" + + o.foo; // Error +>o.foo : never +>o : Thing | undefined +>foo : never + } + else { + o.foo; +>o.foo : string | number +>o : Thing +>foo : string | number + } + if (typeof o?.foo != "undefined") { +>typeof o?.foo != "undefined" : boolean +>typeof o?.foo : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>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 + } + else { + o.foo; // Error +>o.foo : never +>o : Thing | undefined +>foo : never + } +} + +declare function assert(x: unknown): asserts x; +>assert : (x: unknown) => asserts x +>x : unknown + +declare function assertNonNull(x: T): asserts x is NonNullable; +>assertNonNull : (x: T) => asserts x is NonNullable +>x : T + +function f30(o: Thing | undefined) { +>f30 : (o: Thing | undefined) => void +>o : Thing | undefined + + if (!!true) { +>!!true : true +>!true : false +>true : true + + assert(o?.foo); +>assert(o?.foo) : void +>assert : (x: unknown) => asserts x +>o?.foo : string | number | undefined +>o : Thing | undefined +>foo : string | number | undefined + + o.foo; +>o.foo : string | number +>o : Thing +>foo : string | number + } + if (!!true) { +>!!true : true +>!true : false +>true : true + + assert(o?.foo === 42); +>assert(o?.foo === 42) : void +>assert : (x: unknown) => asserts x +>o?.foo === 42 : boolean +>o?.foo : string | number | undefined +>o : Thing | undefined +>foo : string | number | undefined +>42 : 42 + + o.foo; +>o.foo : 42 +>o : Thing +>foo : 42 + } + if (!!true) { +>!!true : true +>!true : false +>true : true + + assert(typeof o?.foo === "number"); +>assert(typeof o?.foo === "number") : void +>assert : (x: unknown) => asserts x +>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 (!!true) { +>!!true : true +>!true : false +>true : true + + assertNonNull(o?.foo); +>assertNonNull(o?.foo) : void +>assertNonNull : (x: T) => asserts x is NonNullable +>o?.foo : string | number | undefined +>o : Thing | undefined +>foo : string | number | undefined + + o.foo; +>o.foo : string | number +>o : Thing +>foo : string | number + } +} + +function f40(o: Thing | undefined) { +>f40 : (o: Thing | undefined) => void +>o : Thing | undefined + + switch (o?.foo) { +>o?.foo : string | number | undefined +>o : Thing | undefined +>foo : string | number | undefined + + case "abc": +>"abc" : "abc" + + o.foo; +>o.foo : "abc" +>o : Thing +>foo : "abc" + + break; + case 42: +>42 : 42 + + o.foo; +>o.foo : 42 +>o : Thing +>foo : 42 + + break; + case undefined: +>undefined : undefined + + o.foo; // Error +>o.foo : never +>o : Thing | undefined +>foo : never + + break; + default: + o.foo; // Error +>o.foo : string | number +>o : Thing | undefined +>foo : string | number + + break; + } +} + +function f41(o: Thing | undefined) { +>f41 : (o: Thing | undefined) => void +>o : Thing | undefined + + switch (typeof o?.foo) { +>typeof o?.foo : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>o?.foo : string | number | undefined +>o : Thing | undefined +>foo : string | number | undefined + + case "string": +>"string" : "string" + + o.foo; +>o.foo : string +>o : Thing +>foo : string + + break; + case "number": +>"number" : "number" + + o.foo; +>o.foo : number +>o : Thing +>foo : number + + break; + case "undefined": +>"undefined" : "undefined" + + o.foo; // Error +>o.foo : never +>o : Thing | undefined +>foo : never + + break; + default: + o.foo; // Error +>o.foo : never +>o : Thing | undefined +>foo : never + + break; + } +} + +// Repros from #34570 + +type Shape = +>Shape : Shape + + | { type: 'rectangle', width: number, height: number } +>type : "rectangle" +>width : number +>height : number + + | { type: 'circle', radius: number } +>type : "circle" +>radius : number + +function getArea(shape?: Shape) { +>getArea : (shape?: { type: "rectangle"; width: number; height: number; } | { type: "circle"; radius: number; } | undefined) => number +>shape : { type: "rectangle"; width: number; height: number; } | { type: "circle"; radius: number; } | undefined + + switch (shape?.type) { +>shape?.type : "rectangle" | "circle" | undefined +>shape : { type: "rectangle"; width: number; height: number; } | { type: "circle"; radius: number; } | undefined +>type : "rectangle" | "circle" | undefined + + case 'circle': +>'circle' : "circle" + + return Math.PI * shape.radius ** 2 +>Math.PI * shape.radius ** 2 : number +>Math.PI : number +>Math : Math +>PI : number +>shape.radius ** 2 : number +>shape.radius : number +>shape : { type: "circle"; radius: number; } +>radius : number +>2 : 2 + + case 'rectangle': +>'rectangle' : "rectangle" + + return shape.width * shape.height +>shape.width * shape.height : number +>shape.width : number +>shape : { type: "rectangle"; width: number; height: number; } +>width : number +>shape.height : number +>shape : { type: "rectangle"; width: number; height: number; } +>height : number + + default: + return 0 +>0 : 0 + } +} + +type Feature = { +>Feature : Feature + + id: string; +>id : string + + geometry?: { +>geometry : { type: string; coordinates: number[]; } | undefined + + type: string; +>type : string + + coordinates: number[]; +>coordinates : number[] + + }; +}; + + +function extractCoordinates(f: Feature): number[] { +>extractCoordinates : (f: Feature) => number[] +>f : Feature + + if (f.geometry?.type !== 'test') { +>f.geometry?.type !== 'test' : boolean +>f.geometry?.type : string | undefined +>f.geometry : { type: string; coordinates: number[]; } | undefined +>f : Feature +>geometry : { type: string; coordinates: number[]; } | undefined +>type : string | undefined +>'test' : "test" + + return []; +>[] : never[] + } + return f.geometry.coordinates; +>f.geometry.coordinates : number[] +>f.geometry : { type: string; coordinates: number[]; } +>f : Feature +>geometry : { type: string; coordinates: number[]; } +>coordinates : number[] +} + diff --git a/tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts b/tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts index f54275f309c34..49ebd3807db7f 100644 --- a/tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts +++ b/tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts @@ -302,6 +302,60 @@ function f14(o: Thing | null) { } } +function f15(o: Thing | undefined, value: number) { + if (o?.foo === value) { + o.foo; + } + else { + o.foo; // Error + } + if (o?.foo !== value) { + o.foo; // Error + } + else { + o.foo; + } + if (o?.foo == value) { + o.foo; + } + else { + o.foo; // Error + } + if (o?.foo != value) { + o.foo; // Error + } + else { + o.foo; + } +} + +function f16(o: Thing | undefined) { + if (o?.foo === undefined) { + o.foo; // Error + } + else { + o.foo; + } + if (o?.foo !== undefined) { + o.foo; + } + else { + o.foo; // Error + } + if (o?.foo == undefined) { + o.foo; // Error + } + else { + o.foo; + } + if (o?.foo != undefined) { + o.foo; + } + else { + o.foo; // Error + } +} + function f20(o: Thing | undefined) { if (typeof o?.foo === "number") { o.foo; @@ -331,3 +385,146 @@ function f21(o: Thing | null) { o.baz; } } + +function f22(o: Thing | undefined) { + if (typeof o?.foo === "number") { + o.foo; + } + else { + o.foo; // Error + } + if (typeof o?.foo !== "number") { + o.foo; // Error + } + else { + o.foo; + } + if (typeof o?.foo == "number") { + o.foo; + } + else { + o.foo; // Error + } + if (typeof o?.foo != "number") { + o.foo; // Error + } + else { + o.foo; + } +} + +function f23(o: Thing | undefined) { + if (typeof o?.foo === "undefined") { + o.foo; // Error + } + else { + o.foo; + } + if (typeof o?.foo !== "undefined") { + o.foo; + } + else { + o.foo; // Error + } + if (typeof o?.foo == "undefined") { + o.foo; // Error + } + else { + o.foo; + } + if (typeof o?.foo != "undefined") { + o.foo; + } + else { + o.foo; // Error + } +} + +declare function assert(x: unknown): asserts x; +declare function assertNonNull(x: T): asserts x is NonNullable; + +function f30(o: Thing | undefined) { + if (!!true) { + assert(o?.foo); + o.foo; + } + if (!!true) { + assert(o?.foo === 42); + o.foo; + } + if (!!true) { + assert(typeof o?.foo === "number"); + o.foo; + } + if (!!true) { + assertNonNull(o?.foo); + o.foo; + } +} + +function f40(o: Thing | undefined) { + switch (o?.foo) { + case "abc": + o.foo; + break; + case 42: + o.foo; + break; + case undefined: + o.foo; // Error + break; + default: + o.foo; // Error + break; + } +} + +function f41(o: Thing | undefined) { + switch (typeof o?.foo) { + case "string": + o.foo; + break; + case "number": + o.foo; + break; + case "undefined": + o.foo; // Error + break; + default: + o.foo; // Error + break; + } +} + +// Repros from #34570 + +type Shape = + | { type: 'rectangle', width: number, height: number } + | { type: 'circle', radius: number } + +function getArea(shape?: Shape) { + switch (shape?.type) { + case 'circle': + return Math.PI * shape.radius ** 2 + case 'rectangle': + return shape.width * shape.height + default: + return 0 + } +} + +type Feature = { + id: string; + geometry?: { + type: string; + coordinates: number[]; + }; +}; + + +function extractCoordinates(f: Feature): number[] { + if (f.geometry?.type !== 'test') { + return []; + } + return f.geometry.coordinates; +}