diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 49c45384e6d..5966cdc0e54 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -22599,10 +22599,13 @@ namespace ts { if (propName === undefined) { return type; } - const propType = getTypeOfPropertyOfType(type, propName); + const includesUndefined = strictNullChecks && maybeTypeOfKind(type, TypeFlags.Undefined); + const removeOptional = includesUndefined && isOptionalChain(access); + let propType = getTypeOfPropertyOfType(removeOptional ? getTypeWithFacts(type, TypeFacts.NEUndefined) : type, propName); if (!propType) { return type; } + propType = removeOptional ? getOptionalType(propType) : propType; const narrowedPropType = narrowType(propType); return filterType(type, t => { const discriminantType = getTypeOfPropertyOrIndexSignature(t, propName); diff --git a/tests/baselines/reference/controlFlowOptionalChain.symbols b/tests/baselines/reference/controlFlowOptionalChain.symbols index 5354a1ef95b..be03f36c67d 100644 --- a/tests/baselines/reference/controlFlowOptionalChain.symbols +++ b/tests/baselines/reference/controlFlowOptionalChain.symbols @@ -200,14 +200,14 @@ else { >o3 : Symbol(o3, Decl(controlFlowOptionalChain.ts, 58, 13)) o3?.x; ->o3?.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 58, 19), Decl(controlFlowOptionalChain.ts, 58, 41)) +>o3?.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 58, 41)) >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, 41)) o3.x; ->o3.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 58, 19), Decl(controlFlowOptionalChain.ts, 58, 41)) +>o3.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 58, 41)) >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, 41)) } o3; >o3 : Symbol(o3, Decl(controlFlowOptionalChain.ts, 58, 13)) diff --git a/tests/baselines/reference/controlFlowOptionalChain.types b/tests/baselines/reference/controlFlowOptionalChain.types index 9b22d5149d9..4e335fca6d2 100644 --- a/tests/baselines/reference/controlFlowOptionalChain.types +++ b/tests/baselines/reference/controlFlowOptionalChain.types @@ -223,16 +223,16 @@ if (o3?.x === 1) { } else { o3; ->o3 : { x: 1; y: string; } | { x: 2; y: number; } | undefined +>o3 : { x: 2; y: number; } | undefined o3?.x; >o3?.x : 2 | undefined ->o3 : { x: 1; y: string; } | { x: 2; y: number; } | undefined +>o3 : { x: 2; y: number; } | undefined >x : 2 | undefined o3.x; >o3.x : 2 ->o3 : { x: 1; y: string; } | { x: 2; y: number; } | undefined +>o3 : { x: 2; y: number; } | undefined >x : 2 } o3; diff --git a/tests/baselines/reference/controlFlowOptionalChain2.js b/tests/baselines/reference/controlFlowOptionalChain2.js new file mode 100644 index 00000000000..0f8478b90fa --- /dev/null +++ b/tests/baselines/reference/controlFlowOptionalChain2.js @@ -0,0 +1,30 @@ +//// [controlFlowOptionalChain2.ts] +type A = { + type: 'A'; + name: string; +} + +type B = { + type: 'B'; +} + +function funcTwo(arg: A | B | undefined) { + if (arg?.type === 'B') { + arg; // `B` + return; + } + + arg; + arg?.name; +} + + +//// [controlFlowOptionalChain2.js] +function funcTwo(arg) { + if ((arg === null || arg === void 0 ? void 0 : arg.type) === 'B') { + arg; // `B` + return; + } + arg; + arg === null || arg === void 0 ? void 0 : arg.name; +} diff --git a/tests/baselines/reference/controlFlowOptionalChain2.symbols b/tests/baselines/reference/controlFlowOptionalChain2.symbols new file mode 100644 index 00000000000..f30a8673e45 --- /dev/null +++ b/tests/baselines/reference/controlFlowOptionalChain2.symbols @@ -0,0 +1,44 @@ +=== tests/cases/conformance/controlFlow/controlFlowOptionalChain2.ts === +type A = { +>A : Symbol(A, Decl(controlFlowOptionalChain2.ts, 0, 0)) + + type: 'A'; +>type : Symbol(type, Decl(controlFlowOptionalChain2.ts, 0, 10)) + + name: string; +>name : Symbol(name, Decl(controlFlowOptionalChain2.ts, 1, 12)) +} + +type B = { +>B : Symbol(B, Decl(controlFlowOptionalChain2.ts, 3, 1)) + + type: 'B'; +>type : Symbol(type, Decl(controlFlowOptionalChain2.ts, 5, 10)) +} + +function funcTwo(arg: A | B | undefined) { +>funcTwo : Symbol(funcTwo, Decl(controlFlowOptionalChain2.ts, 7, 1)) +>arg : Symbol(arg, Decl(controlFlowOptionalChain2.ts, 9, 17)) +>A : Symbol(A, Decl(controlFlowOptionalChain2.ts, 0, 0)) +>B : Symbol(B, Decl(controlFlowOptionalChain2.ts, 3, 1)) + + if (arg?.type === 'B') { +>arg?.type : Symbol(type, Decl(controlFlowOptionalChain2.ts, 0, 10), Decl(controlFlowOptionalChain2.ts, 5, 10)) +>arg : Symbol(arg, Decl(controlFlowOptionalChain2.ts, 9, 17)) +>type : Symbol(type, Decl(controlFlowOptionalChain2.ts, 0, 10), Decl(controlFlowOptionalChain2.ts, 5, 10)) + + arg; // `B` +>arg : Symbol(arg, Decl(controlFlowOptionalChain2.ts, 9, 17)) + + return; + } + + arg; +>arg : Symbol(arg, Decl(controlFlowOptionalChain2.ts, 9, 17)) + + arg?.name; +>arg?.name : Symbol(name, Decl(controlFlowOptionalChain2.ts, 1, 12)) +>arg : Symbol(arg, Decl(controlFlowOptionalChain2.ts, 9, 17)) +>name : Symbol(name, Decl(controlFlowOptionalChain2.ts, 1, 12)) +} + diff --git a/tests/baselines/reference/controlFlowOptionalChain2.types b/tests/baselines/reference/controlFlowOptionalChain2.types new file mode 100644 index 00000000000..6e41060218e --- /dev/null +++ b/tests/baselines/reference/controlFlowOptionalChain2.types @@ -0,0 +1,44 @@ +=== tests/cases/conformance/controlFlow/controlFlowOptionalChain2.ts === +type A = { +>A : A + + type: 'A'; +>type : "A" + + name: string; +>name : string +} + +type B = { +>B : B + + type: 'B'; +>type : "B" +} + +function funcTwo(arg: A | B | undefined) { +>funcTwo : (arg: A | B | undefined) => void +>arg : A | B | undefined + + if (arg?.type === 'B') { +>arg?.type === 'B' : boolean +>arg?.type : "A" | "B" | undefined +>arg : A | B | undefined +>type : "A" | "B" | undefined +>'B' : "B" + + arg; // `B` +>arg : B + + return; + } + + arg; +>arg : A | undefined + + arg?.name; +>arg?.name : string | undefined +>arg : A | undefined +>name : string | undefined +} + diff --git a/tests/cases/conformance/controlFlow/controlFlowOptionalChain2.ts b/tests/cases/conformance/controlFlow/controlFlowOptionalChain2.ts new file mode 100644 index 00000000000..a48d627ee32 --- /dev/null +++ b/tests/cases/conformance/controlFlow/controlFlowOptionalChain2.ts @@ -0,0 +1,20 @@ +// @strictNullChecks: true + +type A = { + type: 'A'; + name: string; +} + +type B = { + type: 'B'; +} + +function funcTwo(arg: A | B | undefined) { + if (arg?.type === 'B') { + arg; // `B` + return; + } + + arg; + arg?.name; +}