mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-07 14:13:54 -06:00
* Fix #16778 - use previous type to check discriminable type and not declared type * Rename prevType -> computedType
This commit is contained in:
parent
b9fe9964d2
commit
b080aa9440
@ -11500,7 +11500,7 @@ namespace ts {
|
||||
if (isMatchingReference(reference, expr)) {
|
||||
type = narrowTypeBySwitchOnDiscriminant(type, flow.switchStatement, flow.clauseStart, flow.clauseEnd);
|
||||
}
|
||||
else if (isMatchingReferenceDiscriminant(expr)) {
|
||||
else if (isMatchingReferenceDiscriminant(expr, type)) {
|
||||
type = narrowTypeByDiscriminant(type, <PropertyAccessExpression>expr, t => narrowTypeBySwitchOnDiscriminant(t, flow.switchStatement, flow.clauseStart, flow.clauseEnd));
|
||||
}
|
||||
return createFlowType(type, isIncomplete(flowType));
|
||||
@ -11620,11 +11620,11 @@ namespace ts {
|
||||
return result;
|
||||
}
|
||||
|
||||
function isMatchingReferenceDiscriminant(expr: Expression) {
|
||||
function isMatchingReferenceDiscriminant(expr: Expression, computedType: Type) {
|
||||
return expr.kind === SyntaxKind.PropertyAccessExpression &&
|
||||
declaredType.flags & TypeFlags.Union &&
|
||||
computedType.flags & TypeFlags.Union &&
|
||||
isMatchingReference(reference, (<PropertyAccessExpression>expr).expression) &&
|
||||
isDiscriminantProperty(declaredType, (<PropertyAccessExpression>expr).name.escapedText);
|
||||
isDiscriminantProperty(computedType, (<PropertyAccessExpression>expr).name.escapedText);
|
||||
}
|
||||
|
||||
function narrowTypeByDiscriminant(type: Type, propAccess: PropertyAccessExpression, narrowType: (t: Type) => Type): Type {
|
||||
@ -11638,7 +11638,7 @@ namespace ts {
|
||||
if (isMatchingReference(reference, expr)) {
|
||||
return getTypeWithFacts(type, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy);
|
||||
}
|
||||
if (isMatchingReferenceDiscriminant(expr)) {
|
||||
if (isMatchingReferenceDiscriminant(expr, declaredType)) {
|
||||
return narrowTypeByDiscriminant(type, <PropertyAccessExpression>expr, t => getTypeWithFacts(t, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy));
|
||||
}
|
||||
if (containsMatchingReferenceDiscriminant(reference, expr)) {
|
||||
@ -11670,10 +11670,10 @@ namespace ts {
|
||||
if (isMatchingReference(reference, right)) {
|
||||
return narrowTypeByEquality(type, operator, left, assumeTrue);
|
||||
}
|
||||
if (isMatchingReferenceDiscriminant(left)) {
|
||||
if (isMatchingReferenceDiscriminant(left, declaredType)) {
|
||||
return narrowTypeByDiscriminant(type, <PropertyAccessExpression>left, t => narrowTypeByEquality(t, operator, right, assumeTrue));
|
||||
}
|
||||
if (isMatchingReferenceDiscriminant(right)) {
|
||||
if (isMatchingReferenceDiscriminant(right, declaredType)) {
|
||||
return narrowTypeByDiscriminant(type, <PropertyAccessExpression>right, t => narrowTypeByEquality(t, operator, left, assumeTrue));
|
||||
}
|
||||
if (containsMatchingReferenceDiscriminant(reference, left) || containsMatchingReferenceDiscriminant(reference, right)) {
|
||||
|
||||
58
tests/baselines/reference/flowControlTypeGuardThenSwitch.js
Normal file
58
tests/baselines/reference/flowControlTypeGuardThenSwitch.js
Normal file
@ -0,0 +1,58 @@
|
||||
//// [flowControlTypeGuardThenSwitch.ts]
|
||||
enum Kind {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
interface Base {
|
||||
kind: Kind;
|
||||
}
|
||||
|
||||
interface A extends Base {
|
||||
kind: Kind.A;
|
||||
yar: any;
|
||||
}
|
||||
|
||||
interface B extends Base {
|
||||
kind: Kind.B;
|
||||
gar: any;
|
||||
}
|
||||
|
||||
type Both = A | B;
|
||||
function isBoth(x: Base): x is Both {
|
||||
return true;
|
||||
}
|
||||
|
||||
let foo: Base = undefined;
|
||||
if (isBoth(foo)) {
|
||||
switch (foo.kind) {
|
||||
case Kind.A:
|
||||
const myA: A = foo; // Should not be an error
|
||||
break;
|
||||
case Kind.B:
|
||||
const myB: B = foo;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//// [flowControlTypeGuardThenSwitch.js]
|
||||
var Kind;
|
||||
(function (Kind) {
|
||||
Kind[Kind["A"] = 0] = "A";
|
||||
Kind[Kind["B"] = 1] = "B";
|
||||
})(Kind || (Kind = {}));
|
||||
function isBoth(x) {
|
||||
return true;
|
||||
}
|
||||
var foo = undefined;
|
||||
if (isBoth(foo)) {
|
||||
switch (foo.kind) {
|
||||
case Kind.A:
|
||||
var myA = foo; // Should not be an error
|
||||
break;
|
||||
case Kind.B:
|
||||
var myB = foo;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,99 @@
|
||||
=== tests/cases/compiler/flowControlTypeGuardThenSwitch.ts ===
|
||||
enum Kind {
|
||||
>Kind : Symbol(Kind, Decl(flowControlTypeGuardThenSwitch.ts, 0, 0))
|
||||
|
||||
A,
|
||||
>A : Symbol(Kind.A, Decl(flowControlTypeGuardThenSwitch.ts, 0, 11))
|
||||
|
||||
B,
|
||||
>B : Symbol(Kind.B, Decl(flowControlTypeGuardThenSwitch.ts, 1, 6))
|
||||
}
|
||||
|
||||
interface Base {
|
||||
>Base : Symbol(Base, Decl(flowControlTypeGuardThenSwitch.ts, 3, 1))
|
||||
|
||||
kind: Kind;
|
||||
>kind : Symbol(Base.kind, Decl(flowControlTypeGuardThenSwitch.ts, 5, 16))
|
||||
>Kind : Symbol(Kind, Decl(flowControlTypeGuardThenSwitch.ts, 0, 0))
|
||||
}
|
||||
|
||||
interface A extends Base {
|
||||
>A : Symbol(A, Decl(flowControlTypeGuardThenSwitch.ts, 7, 1))
|
||||
>Base : Symbol(Base, Decl(flowControlTypeGuardThenSwitch.ts, 3, 1))
|
||||
|
||||
kind: Kind.A;
|
||||
>kind : Symbol(A.kind, Decl(flowControlTypeGuardThenSwitch.ts, 9, 26))
|
||||
>Kind : Symbol(Kind, Decl(flowControlTypeGuardThenSwitch.ts, 0, 0))
|
||||
>A : Symbol(Kind.A, Decl(flowControlTypeGuardThenSwitch.ts, 0, 11))
|
||||
|
||||
yar: any;
|
||||
>yar : Symbol(A.yar, Decl(flowControlTypeGuardThenSwitch.ts, 10, 17))
|
||||
}
|
||||
|
||||
interface B extends Base {
|
||||
>B : Symbol(B, Decl(flowControlTypeGuardThenSwitch.ts, 12, 1))
|
||||
>Base : Symbol(Base, Decl(flowControlTypeGuardThenSwitch.ts, 3, 1))
|
||||
|
||||
kind: Kind.B;
|
||||
>kind : Symbol(B.kind, Decl(flowControlTypeGuardThenSwitch.ts, 14, 26))
|
||||
>Kind : Symbol(Kind, Decl(flowControlTypeGuardThenSwitch.ts, 0, 0))
|
||||
>B : Symbol(Kind.B, Decl(flowControlTypeGuardThenSwitch.ts, 1, 6))
|
||||
|
||||
gar: any;
|
||||
>gar : Symbol(B.gar, Decl(flowControlTypeGuardThenSwitch.ts, 15, 17))
|
||||
}
|
||||
|
||||
type Both = A | B;
|
||||
>Both : Symbol(Both, Decl(flowControlTypeGuardThenSwitch.ts, 17, 1))
|
||||
>A : Symbol(A, Decl(flowControlTypeGuardThenSwitch.ts, 7, 1))
|
||||
>B : Symbol(B, Decl(flowControlTypeGuardThenSwitch.ts, 12, 1))
|
||||
|
||||
function isBoth(x: Base): x is Both {
|
||||
>isBoth : Symbol(isBoth, Decl(flowControlTypeGuardThenSwitch.ts, 19, 18))
|
||||
>x : Symbol(x, Decl(flowControlTypeGuardThenSwitch.ts, 20, 16))
|
||||
>Base : Symbol(Base, Decl(flowControlTypeGuardThenSwitch.ts, 3, 1))
|
||||
>x : Symbol(x, Decl(flowControlTypeGuardThenSwitch.ts, 20, 16))
|
||||
>Both : Symbol(Both, Decl(flowControlTypeGuardThenSwitch.ts, 17, 1))
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
let foo: Base = undefined;
|
||||
>foo : Symbol(foo, Decl(flowControlTypeGuardThenSwitch.ts, 24, 3))
|
||||
>Base : Symbol(Base, Decl(flowControlTypeGuardThenSwitch.ts, 3, 1))
|
||||
>undefined : Symbol(undefined)
|
||||
|
||||
if (isBoth(foo)) {
|
||||
>isBoth : Symbol(isBoth, Decl(flowControlTypeGuardThenSwitch.ts, 19, 18))
|
||||
>foo : Symbol(foo, Decl(flowControlTypeGuardThenSwitch.ts, 24, 3))
|
||||
|
||||
switch (foo.kind) {
|
||||
>foo.kind : Symbol(kind, Decl(flowControlTypeGuardThenSwitch.ts, 9, 26), Decl(flowControlTypeGuardThenSwitch.ts, 14, 26))
|
||||
>foo : Symbol(foo, Decl(flowControlTypeGuardThenSwitch.ts, 24, 3))
|
||||
>kind : Symbol(kind, Decl(flowControlTypeGuardThenSwitch.ts, 9, 26), Decl(flowControlTypeGuardThenSwitch.ts, 14, 26))
|
||||
|
||||
case Kind.A:
|
||||
>Kind.A : Symbol(Kind.A, Decl(flowControlTypeGuardThenSwitch.ts, 0, 11))
|
||||
>Kind : Symbol(Kind, Decl(flowControlTypeGuardThenSwitch.ts, 0, 0))
|
||||
>A : Symbol(Kind.A, Decl(flowControlTypeGuardThenSwitch.ts, 0, 11))
|
||||
|
||||
const myA: A = foo; // Should not be an error
|
||||
>myA : Symbol(myA, Decl(flowControlTypeGuardThenSwitch.ts, 28, 17))
|
||||
>A : Symbol(A, Decl(flowControlTypeGuardThenSwitch.ts, 7, 1))
|
||||
>foo : Symbol(foo, Decl(flowControlTypeGuardThenSwitch.ts, 24, 3))
|
||||
|
||||
break;
|
||||
case Kind.B:
|
||||
>Kind.B : Symbol(Kind.B, Decl(flowControlTypeGuardThenSwitch.ts, 1, 6))
|
||||
>Kind : Symbol(Kind, Decl(flowControlTypeGuardThenSwitch.ts, 0, 0))
|
||||
>B : Symbol(Kind.B, Decl(flowControlTypeGuardThenSwitch.ts, 1, 6))
|
||||
|
||||
const myB: B = foo;
|
||||
>myB : Symbol(myB, Decl(flowControlTypeGuardThenSwitch.ts, 31, 17))
|
||||
>B : Symbol(B, Decl(flowControlTypeGuardThenSwitch.ts, 12, 1))
|
||||
>foo : Symbol(foo, Decl(flowControlTypeGuardThenSwitch.ts, 24, 3))
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
101
tests/baselines/reference/flowControlTypeGuardThenSwitch.types
Normal file
101
tests/baselines/reference/flowControlTypeGuardThenSwitch.types
Normal file
@ -0,0 +1,101 @@
|
||||
=== tests/cases/compiler/flowControlTypeGuardThenSwitch.ts ===
|
||||
enum Kind {
|
||||
>Kind : Kind
|
||||
|
||||
A,
|
||||
>A : Kind.A
|
||||
|
||||
B,
|
||||
>B : Kind.B
|
||||
}
|
||||
|
||||
interface Base {
|
||||
>Base : Base
|
||||
|
||||
kind: Kind;
|
||||
>kind : Kind
|
||||
>Kind : Kind
|
||||
}
|
||||
|
||||
interface A extends Base {
|
||||
>A : A
|
||||
>Base : Base
|
||||
|
||||
kind: Kind.A;
|
||||
>kind : Kind.A
|
||||
>Kind : any
|
||||
>A : Kind.A
|
||||
|
||||
yar: any;
|
||||
>yar : any
|
||||
}
|
||||
|
||||
interface B extends Base {
|
||||
>B : B
|
||||
>Base : Base
|
||||
|
||||
kind: Kind.B;
|
||||
>kind : Kind.B
|
||||
>Kind : any
|
||||
>B : Kind.B
|
||||
|
||||
gar: any;
|
||||
>gar : any
|
||||
}
|
||||
|
||||
type Both = A | B;
|
||||
>Both : Both
|
||||
>A : A
|
||||
>B : B
|
||||
|
||||
function isBoth(x: Base): x is Both {
|
||||
>isBoth : (x: Base) => x is Both
|
||||
>x : Base
|
||||
>Base : Base
|
||||
>x : any
|
||||
>Both : Both
|
||||
|
||||
return true;
|
||||
>true : true
|
||||
}
|
||||
|
||||
let foo: Base = undefined;
|
||||
>foo : Base
|
||||
>Base : Base
|
||||
>undefined : undefined
|
||||
|
||||
if (isBoth(foo)) {
|
||||
>isBoth(foo) : boolean
|
||||
>isBoth : (x: Base) => x is Both
|
||||
>foo : Base
|
||||
|
||||
switch (foo.kind) {
|
||||
>foo.kind : Kind
|
||||
>foo : Both
|
||||
>kind : Kind
|
||||
|
||||
case Kind.A:
|
||||
>Kind.A : Kind.A
|
||||
>Kind : typeof Kind
|
||||
>A : Kind.A
|
||||
|
||||
const myA: A = foo; // Should not be an error
|
||||
>myA : A
|
||||
>A : A
|
||||
>foo : A
|
||||
|
||||
break;
|
||||
case Kind.B:
|
||||
>Kind.B : Kind.B
|
||||
>Kind : typeof Kind
|
||||
>B : Kind.B
|
||||
|
||||
const myB: B = foo;
|
||||
>myB : B
|
||||
>B : B
|
||||
>foo : B
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
35
tests/cases/compiler/flowControlTypeGuardThenSwitch.ts
Normal file
35
tests/cases/compiler/flowControlTypeGuardThenSwitch.ts
Normal file
@ -0,0 +1,35 @@
|
||||
enum Kind {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
interface Base {
|
||||
kind: Kind;
|
||||
}
|
||||
|
||||
interface A extends Base {
|
||||
kind: Kind.A;
|
||||
yar: any;
|
||||
}
|
||||
|
||||
interface B extends Base {
|
||||
kind: Kind.B;
|
||||
gar: any;
|
||||
}
|
||||
|
||||
type Both = A | B;
|
||||
function isBoth(x: Base): x is Both {
|
||||
return true;
|
||||
}
|
||||
|
||||
let foo: Base = undefined;
|
||||
if (isBoth(foo)) {
|
||||
switch (foo.kind) {
|
||||
case Kind.A:
|
||||
const myA: A = foo; // Should not be an error
|
||||
break;
|
||||
case Kind.B:
|
||||
const myB: B = foo;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user