Fix discriminant property narrowing through optional chain with null (#42503)

* Fix discriminant property narrowing through optional chain with null

* Accept baselines

* Add tests from Anders
This commit is contained in:
Andrew Branch 2021-01-29 12:03:42 -08:00 committed by GitHub
parent 65e4d60d81
commit c15f40abfa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 660 additions and 4 deletions

View File

@ -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);

View File

@ -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
}
}

View File

@ -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))
}
}

View File

@ -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
}
}

View File

@ -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
}
}