diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 606ec5a55d4..b47410d33f9 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -22641,13 +22641,13 @@ namespace ts { if (propName === undefined) { return type; } - const includesUndefined = strictNullChecks && maybeTypeOfKind(type, TypeFlags.Undefined); - const removeOptional = includesUndefined && isOptionalChain(access); - let propType = getTypeOfPropertyOfType(removeOptional ? getTypeWithFacts(type, TypeFacts.NEUndefined) : type, propName); + const includesNullable = strictNullChecks && maybeTypeOfKind(type, TypeFlags.Nullable); + const removeNullable = includesNullable && isOptionalChain(access); + let propType = getTypeOfPropertyOfType(removeNullable ? getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull) : type, propName); if (!propType) { return type; } - propType = removeOptional ? getOptionalType(propType) : propType; + propType = removeNullable ? getOptionalType(propType) : propType; const narrowedPropType = narrowType(propType); return filterType(type, t => { const discriminantType = getTypeOfPropertyOrIndexSignature(t, propName); diff --git a/tests/baselines/reference/controlFlowOptionalChain2.js b/tests/baselines/reference/controlFlowOptionalChain2.js index 0f8478b90fa..a7cf147854b 100644 --- a/tests/baselines/reference/controlFlowOptionalChain2.js +++ b/tests/baselines/reference/controlFlowOptionalChain2.js @@ -17,6 +17,92 @@ function funcTwo(arg: A | B | undefined) { arg; arg?.name; } + +function funcThree(arg: A | B | null) { + if (arg?.type === 'B') { + arg; // `B` + return; + } + + arg; + arg?.name; +} + +type U = { kind: undefined, u: 'u' } +type N = { kind: null, n: 'n' } +type X = { kind: 'X', x: 'x' } + +function f1(x: X | U | undefined) { + if (x?.kind === undefined) { + x; // U | undefined + } + else { + x; // X + } +} + +function f2(x: X | N | undefined) { + if (x?.kind === undefined) { + x; // undefined + } + else { + x; // X | N + } +} + +function f3(x: X | U | null) { + if (x?.kind === undefined) { + x; // U | null + } + else { + x; // X + } +} + +function f4(x: X | N | null) { + if (x?.kind === undefined) { + x; // null + } + else { + x; // X | N + } +} + +function f5(x: X | U | undefined) { + if (x?.kind === null) { + x; // never + } + else { + x; // X | U | undefined + } +} + +function f6(x: X | N | undefined) { + if (x?.kind === null) { + x; // N + } + else { + x; // X | undefined + } +} + +function f7(x: X | U | null) { + if (x?.kind === null) { + x; // never + } + else { + x; // X | U | null + } +} + +function f8(x: X | N | null) { + if (x?.kind === null) { + x; // N + } + else { + x; // X | null + } +} //// [controlFlowOptionalChain2.js] @@ -28,3 +114,75 @@ function funcTwo(arg) { arg; arg === null || arg === void 0 ? void 0 : arg.name; } +function funcThree(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; +} +function f1(x) { + if ((x === null || x === void 0 ? void 0 : x.kind) === undefined) { + x; // U | undefined + } + else { + x; // X + } +} +function f2(x) { + if ((x === null || x === void 0 ? void 0 : x.kind) === undefined) { + x; // undefined + } + else { + x; // X | N + } +} +function f3(x) { + if ((x === null || x === void 0 ? void 0 : x.kind) === undefined) { + x; // U | null + } + else { + x; // X + } +} +function f4(x) { + if ((x === null || x === void 0 ? void 0 : x.kind) === undefined) { + x; // null + } + else { + x; // X | N + } +} +function f5(x) { + if ((x === null || x === void 0 ? void 0 : x.kind) === null) { + x; // never + } + else { + x; // X | U | undefined + } +} +function f6(x) { + if ((x === null || x === void 0 ? void 0 : x.kind) === null) { + x; // N + } + else { + x; // X | undefined + } +} +function f7(x) { + if ((x === null || x === void 0 ? void 0 : x.kind) === null) { + x; // never + } + else { + x; // X | U | null + } +} +function f8(x) { + if ((x === null || x === void 0 ? void 0 : x.kind) === null) { + x; // N + } + else { + x; // X | null + } +} diff --git a/tests/baselines/reference/controlFlowOptionalChain2.symbols b/tests/baselines/reference/controlFlowOptionalChain2.symbols index f30a8673e45..e4b2184a889 100644 --- a/tests/baselines/reference/controlFlowOptionalChain2.symbols +++ b/tests/baselines/reference/controlFlowOptionalChain2.symbols @@ -42,3 +42,208 @@ function funcTwo(arg: A | B | undefined) { >name : Symbol(name, Decl(controlFlowOptionalChain2.ts, 1, 12)) } +function funcThree(arg: A | B | null) { +>funcThree : Symbol(funcThree, Decl(controlFlowOptionalChain2.ts, 17, 1)) +>arg : Symbol(arg, Decl(controlFlowOptionalChain2.ts, 19, 19)) +>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, 19, 19)) +>type : Symbol(type, Decl(controlFlowOptionalChain2.ts, 0, 10), Decl(controlFlowOptionalChain2.ts, 5, 10)) + + arg; // `B` +>arg : Symbol(arg, Decl(controlFlowOptionalChain2.ts, 19, 19)) + + return; + } + + arg; +>arg : Symbol(arg, Decl(controlFlowOptionalChain2.ts, 19, 19)) + + arg?.name; +>arg?.name : Symbol(name, Decl(controlFlowOptionalChain2.ts, 1, 12)) +>arg : Symbol(arg, Decl(controlFlowOptionalChain2.ts, 19, 19)) +>name : Symbol(name, Decl(controlFlowOptionalChain2.ts, 1, 12)) +} + +type U = { kind: undefined, u: 'u' } +>U : Symbol(U, Decl(controlFlowOptionalChain2.ts, 27, 1)) +>kind : Symbol(kind, Decl(controlFlowOptionalChain2.ts, 29, 10)) +>u : Symbol(u, Decl(controlFlowOptionalChain2.ts, 29, 27)) + +type N = { kind: null, n: 'n' } +>N : Symbol(N, Decl(controlFlowOptionalChain2.ts, 29, 36)) +>kind : Symbol(kind, Decl(controlFlowOptionalChain2.ts, 30, 10)) +>n : Symbol(n, Decl(controlFlowOptionalChain2.ts, 30, 22)) + +type X = { kind: 'X', x: 'x' } +>X : Symbol(X, Decl(controlFlowOptionalChain2.ts, 30, 31)) +>kind : Symbol(kind, Decl(controlFlowOptionalChain2.ts, 31, 10)) +>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 31, 21)) + +function f1(x: X | U | undefined) { +>f1 : Symbol(f1, Decl(controlFlowOptionalChain2.ts, 31, 30)) +>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 33, 12)) +>X : Symbol(X, Decl(controlFlowOptionalChain2.ts, 30, 31)) +>U : Symbol(U, Decl(controlFlowOptionalChain2.ts, 27, 1)) + + if (x?.kind === undefined) { +>x?.kind : Symbol(kind, Decl(controlFlowOptionalChain2.ts, 29, 10), Decl(controlFlowOptionalChain2.ts, 31, 10)) +>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 33, 12)) +>kind : Symbol(kind, Decl(controlFlowOptionalChain2.ts, 29, 10), Decl(controlFlowOptionalChain2.ts, 31, 10)) +>undefined : Symbol(undefined) + + x; // U | undefined +>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 33, 12)) + } + else { + x; // X +>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 33, 12)) + } +} + +function f2(x: X | N | undefined) { +>f2 : Symbol(f2, Decl(controlFlowOptionalChain2.ts, 40, 1)) +>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 42, 12)) +>X : Symbol(X, Decl(controlFlowOptionalChain2.ts, 30, 31)) +>N : Symbol(N, Decl(controlFlowOptionalChain2.ts, 29, 36)) + + if (x?.kind === undefined) { +>x?.kind : Symbol(kind, Decl(controlFlowOptionalChain2.ts, 30, 10), Decl(controlFlowOptionalChain2.ts, 31, 10)) +>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 42, 12)) +>kind : Symbol(kind, Decl(controlFlowOptionalChain2.ts, 30, 10), Decl(controlFlowOptionalChain2.ts, 31, 10)) +>undefined : Symbol(undefined) + + x; // undefined +>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 42, 12)) + } + else { + x; // X | N +>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 42, 12)) + } +} + +function f3(x: X | U | null) { +>f3 : Symbol(f3, Decl(controlFlowOptionalChain2.ts, 49, 1)) +>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 51, 12)) +>X : Symbol(X, Decl(controlFlowOptionalChain2.ts, 30, 31)) +>U : Symbol(U, Decl(controlFlowOptionalChain2.ts, 27, 1)) + + if (x?.kind === undefined) { +>x?.kind : Symbol(kind, Decl(controlFlowOptionalChain2.ts, 29, 10), Decl(controlFlowOptionalChain2.ts, 31, 10)) +>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 51, 12)) +>kind : Symbol(kind, Decl(controlFlowOptionalChain2.ts, 29, 10), Decl(controlFlowOptionalChain2.ts, 31, 10)) +>undefined : Symbol(undefined) + + x; // U | null +>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 51, 12)) + } + else { + x; // X +>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 51, 12)) + } +} + +function f4(x: X | N | null) { +>f4 : Symbol(f4, Decl(controlFlowOptionalChain2.ts, 58, 1)) +>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 60, 12)) +>X : Symbol(X, Decl(controlFlowOptionalChain2.ts, 30, 31)) +>N : Symbol(N, Decl(controlFlowOptionalChain2.ts, 29, 36)) + + if (x?.kind === undefined) { +>x?.kind : Symbol(kind, Decl(controlFlowOptionalChain2.ts, 30, 10), Decl(controlFlowOptionalChain2.ts, 31, 10)) +>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 60, 12)) +>kind : Symbol(kind, Decl(controlFlowOptionalChain2.ts, 30, 10), Decl(controlFlowOptionalChain2.ts, 31, 10)) +>undefined : Symbol(undefined) + + x; // null +>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 60, 12)) + } + else { + x; // X | N +>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 60, 12)) + } +} + +function f5(x: X | U | undefined) { +>f5 : Symbol(f5, Decl(controlFlowOptionalChain2.ts, 67, 1)) +>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 69, 12)) +>X : Symbol(X, Decl(controlFlowOptionalChain2.ts, 30, 31)) +>U : Symbol(U, Decl(controlFlowOptionalChain2.ts, 27, 1)) + + if (x?.kind === null) { +>x?.kind : Symbol(kind, Decl(controlFlowOptionalChain2.ts, 29, 10), Decl(controlFlowOptionalChain2.ts, 31, 10)) +>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 69, 12)) +>kind : Symbol(kind, Decl(controlFlowOptionalChain2.ts, 29, 10), Decl(controlFlowOptionalChain2.ts, 31, 10)) + + x; // never +>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 69, 12)) + } + else { + x; // X | U | undefined +>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 69, 12)) + } +} + +function f6(x: X | N | undefined) { +>f6 : Symbol(f6, Decl(controlFlowOptionalChain2.ts, 76, 1)) +>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 78, 12)) +>X : Symbol(X, Decl(controlFlowOptionalChain2.ts, 30, 31)) +>N : Symbol(N, Decl(controlFlowOptionalChain2.ts, 29, 36)) + + if (x?.kind === null) { +>x?.kind : Symbol(kind, Decl(controlFlowOptionalChain2.ts, 30, 10), Decl(controlFlowOptionalChain2.ts, 31, 10)) +>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 78, 12)) +>kind : Symbol(kind, Decl(controlFlowOptionalChain2.ts, 30, 10), Decl(controlFlowOptionalChain2.ts, 31, 10)) + + x; // N +>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 78, 12)) + } + else { + x; // X | undefined +>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 78, 12)) + } +} + +function f7(x: X | U | null) { +>f7 : Symbol(f7, Decl(controlFlowOptionalChain2.ts, 85, 1)) +>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 87, 12)) +>X : Symbol(X, Decl(controlFlowOptionalChain2.ts, 30, 31)) +>U : Symbol(U, Decl(controlFlowOptionalChain2.ts, 27, 1)) + + if (x?.kind === null) { +>x?.kind : Symbol(kind, Decl(controlFlowOptionalChain2.ts, 29, 10), Decl(controlFlowOptionalChain2.ts, 31, 10)) +>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 87, 12)) +>kind : Symbol(kind, Decl(controlFlowOptionalChain2.ts, 29, 10), Decl(controlFlowOptionalChain2.ts, 31, 10)) + + x; // never +>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 87, 12)) + } + else { + x; // X | U | null +>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 87, 12)) + } +} + +function f8(x: X | N | null) { +>f8 : Symbol(f8, Decl(controlFlowOptionalChain2.ts, 94, 1)) +>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 96, 12)) +>X : Symbol(X, Decl(controlFlowOptionalChain2.ts, 30, 31)) +>N : Symbol(N, Decl(controlFlowOptionalChain2.ts, 29, 36)) + + if (x?.kind === null) { +>x?.kind : Symbol(kind, Decl(controlFlowOptionalChain2.ts, 30, 10), Decl(controlFlowOptionalChain2.ts, 31, 10)) +>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 96, 12)) +>kind : Symbol(kind, Decl(controlFlowOptionalChain2.ts, 30, 10), Decl(controlFlowOptionalChain2.ts, 31, 10)) + + x; // N +>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 96, 12)) + } + else { + x; // X | null +>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 96, 12)) + } +} + diff --git a/tests/baselines/reference/controlFlowOptionalChain2.types b/tests/baselines/reference/controlFlowOptionalChain2.types index 6e41060218e..f3b2d1cd573 100644 --- a/tests/baselines/reference/controlFlowOptionalChain2.types +++ b/tests/baselines/reference/controlFlowOptionalChain2.types @@ -42,3 +42,210 @@ function funcTwo(arg: A | B | undefined) { >name : string | undefined } +function funcThree(arg: A | B | null) { +>funcThree : (arg: A | B | null) => void +>arg : A | B | null +>null : null + + if (arg?.type === 'B') { +>arg?.type === 'B' : boolean +>arg?.type : "A" | "B" | undefined +>arg : A | B | null +>type : "A" | "B" | undefined +>'B' : "B" + + arg; // `B` +>arg : B + + return; + } + + arg; +>arg : A | null + + arg?.name; +>arg?.name : string | undefined +>arg : A | null +>name : string | undefined +} + +type U = { kind: undefined, u: 'u' } +>U : U +>kind : undefined +>u : "u" + +type N = { kind: null, n: 'n' } +>N : N +>kind : null +>null : null +>n : "n" + +type X = { kind: 'X', x: 'x' } +>X : X +>kind : "X" +>x : "x" + +function f1(x: X | U | undefined) { +>f1 : (x: X | U | undefined) => void +>x : U | X | undefined + + if (x?.kind === undefined) { +>x?.kind === undefined : boolean +>x?.kind : "X" | undefined +>x : U | X | undefined +>kind : "X" | undefined +>undefined : undefined + + x; // U | undefined +>x : U | undefined + } + else { + x; // X +>x : X + } +} + +function f2(x: X | N | undefined) { +>f2 : (x: X | N | undefined) => void +>x : N | X | undefined + + if (x?.kind === undefined) { +>x?.kind === undefined : boolean +>x?.kind : "X" | null | undefined +>x : N | X | undefined +>kind : "X" | null | undefined +>undefined : undefined + + x; // undefined +>x : undefined + } + else { + x; // X | N +>x : N | X + } +} + +function f3(x: X | U | null) { +>f3 : (x: X | U | null) => void +>x : U | X | null +>null : null + + if (x?.kind === undefined) { +>x?.kind === undefined : boolean +>x?.kind : "X" | undefined +>x : U | X | null +>kind : "X" | undefined +>undefined : undefined + + x; // U | null +>x : U | null + } + else { + x; // X +>x : X + } +} + +function f4(x: X | N | null) { +>f4 : (x: X | N | null) => void +>x : N | X | null +>null : null + + if (x?.kind === undefined) { +>x?.kind === undefined : boolean +>x?.kind : "X" | null | undefined +>x : N | X | null +>kind : "X" | null | undefined +>undefined : undefined + + x; // null +>x : null + } + else { + x; // X | N +>x : N | X + } +} + +function f5(x: X | U | undefined) { +>f5 : (x: X | U | undefined) => void +>x : U | X | undefined + + if (x?.kind === null) { +>x?.kind === null : boolean +>x?.kind : "X" | undefined +>x : U | X | undefined +>kind : "X" | undefined +>null : null + + x; // never +>x : never + } + else { + x; // X | U | undefined +>x : U | X | undefined + } +} + +function f6(x: X | N | undefined) { +>f6 : (x: X | N | undefined) => void +>x : N | X | undefined + + if (x?.kind === null) { +>x?.kind === null : boolean +>x?.kind : "X" | null | undefined +>x : N | X | undefined +>kind : "X" | null | undefined +>null : null + + x; // N +>x : N + } + else { + x; // X | undefined +>x : X | undefined + } +} + +function f7(x: X | U | null) { +>f7 : (x: X | U | null) => void +>x : U | X | null +>null : null + + if (x?.kind === null) { +>x?.kind === null : boolean +>x?.kind : "X" | undefined +>x : U | X | null +>kind : "X" | undefined +>null : null + + x; // never +>x : never + } + else { + x; // X | U | null +>x : U | X | null + } +} + +function f8(x: X | N | null) { +>f8 : (x: X | N | null) => void +>x : N | X | null +>null : null + + if (x?.kind === null) { +>x?.kind === null : boolean +>x?.kind : "X" | null | undefined +>x : N | X | null +>kind : "X" | null | undefined +>null : null + + x; // N +>x : N + } + else { + x; // X | null +>x : X | null + } +} + diff --git a/tests/cases/conformance/controlFlow/controlFlowOptionalChain2.ts b/tests/cases/conformance/controlFlow/controlFlowOptionalChain2.ts index a48d627ee32..b3dc7a14561 100644 --- a/tests/cases/conformance/controlFlow/controlFlowOptionalChain2.ts +++ b/tests/cases/conformance/controlFlow/controlFlowOptionalChain2.ts @@ -18,3 +18,89 @@ function funcTwo(arg: A | B | undefined) { arg; arg?.name; } + +function funcThree(arg: A | B | null) { + if (arg?.type === 'B') { + arg; // `B` + return; + } + + arg; + arg?.name; +} + +type U = { kind: undefined, u: 'u' } +type N = { kind: null, n: 'n' } +type X = { kind: 'X', x: 'x' } + +function f1(x: X | U | undefined) { + if (x?.kind === undefined) { + x; // U | undefined + } + else { + x; // X + } +} + +function f2(x: X | N | undefined) { + if (x?.kind === undefined) { + x; // undefined + } + else { + x; // X | N + } +} + +function f3(x: X | U | null) { + if (x?.kind === undefined) { + x; // U | null + } + else { + x; // X + } +} + +function f4(x: X | N | null) { + if (x?.kind === undefined) { + x; // null + } + else { + x; // X | N + } +} + +function f5(x: X | U | undefined) { + if (x?.kind === null) { + x; // never + } + else { + x; // X | U | undefined + } +} + +function f6(x: X | N | undefined) { + if (x?.kind === null) { + x; // N + } + else { + x; // X | undefined + } +} + +function f7(x: X | U | null) { + if (x?.kind === null) { + x; // never + } + else { + x; // X | U | null + } +} + +function f8(x: X | N | null) { + if (x?.kind === null) { + x; // N + } + else { + x; // X | null + } +}