Remove CFA discriminant check restrictions (#36114)

* Remove unnecessary containsMatchingReferenceDiscriminant logic

* Accept new baselines
This commit is contained in:
Anders Hejlsberg 2020-01-10 09:41:16 -08:00 committed by GitHub
parent 79dcd3dba1
commit 94271aa753
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 4 additions and 227 deletions

View File

@ -18452,32 +18452,6 @@ namespace ts {
return false;
}
// Return true if target is a property access xxx.yyy, source is a property access xxx.zzz, the declared
// type of xxx is a union type, and yyy is a property that is possibly a discriminant. We consider a property
// a possible discriminant if its type differs in the constituents of containing union type, and if every
// choice is a unit type or a union of unit types.
function containsMatchingReferenceDiscriminant(source: Node, target: Node) {
let name;
return isAccessExpression(target) &&
containsMatchingReference(source, target.expression) &&
(name = getAccessedPropertyName(target)) !== undefined &&
isDiscriminantProperty(getDeclaredTypeOfReference(target.expression), name);
}
function getDeclaredTypeOfReference(expr: Node): Type | undefined {
if (expr.kind === SyntaxKind.Identifier) {
return getTypeOfSymbol(getResolvedSymbol(<Identifier>expr));
}
if (isAccessExpression(expr)) {
const type = getDeclaredTypeOfReference(expr.expression);
if (type) {
const propName = getAccessedPropertyName(expr);
return propName !== undefined ? getTypeOfPropertyOfType(type, propName) : undefined;
}
}
return undefined;
}
function isDiscriminantProperty(type: Type | undefined, name: __String) {
if (type && type.flags & TypeFlags.Union) {
const prop = getUnionOrIntersectionProperty(<UnionType>type, name);
@ -19509,9 +19483,6 @@ namespace ts {
type = narrowTypeByDiscriminant(type, expr as AccessExpression,
t => narrowTypeBySwitchOnDiscriminant(t, flow.switchStatement, flow.clauseStart, flow.clauseEnd));
}
else if (containsMatchingReferenceDiscriminant(reference, expr)) {
type = declaredType;
}
}
return createFlowType(type, isIncomplete(flowType));
}
@ -19696,9 +19667,6 @@ namespace ts {
if (isMatchingReferenceDiscriminant(expr, declaredType)) {
return narrowTypeByDiscriminant(type, <AccessExpression>expr, t => getTypeWithFacts(t, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy));
}
if (containsMatchingReferenceDiscriminant(reference, expr)) {
return declaredType;
}
return type;
}
@ -19758,9 +19726,6 @@ namespace ts {
if (isMatchingReferenceDiscriminant(right, declaredType)) {
return narrowTypeByDiscriminant(type, <AccessExpression>right, t => narrowTypeByEquality(t, operator, left, assumeTrue));
}
if (containsMatchingReferenceDiscriminant(reference, left) || containsMatchingReferenceDiscriminant(reference, right)) {
return declaredType;
}
break;
case SyntaxKind.InstanceOfKeyword:
return narrowTypeByInstanceof(type, expr, assumeTrue);
@ -20172,9 +20137,6 @@ namespace ts {
if (isMatchingReferenceDiscriminant(expr, declaredType)) {
return narrowTypeByDiscriminant(type, <AccessExpression>expr, t => getTypeWithFacts(t, assumePresent ? TypeFacts.NEUndefinedOrNull : TypeFacts.EQUndefinedOrNull));
}
if (containsMatchingReferenceDiscriminant(reference, expr)) {
return declaredType;
}
return type;
}
}

View File

@ -1,185 +0,0 @@
tests/cases/compiler/discriminantPropertyCheck.ts(29,9): error TS2532: Object is possibly 'undefined'.
tests/cases/compiler/discriminantPropertyCheck.ts(65,9): error TS2532: Object is possibly 'undefined'.
==== tests/cases/compiler/discriminantPropertyCheck.ts (2 errors) ====
type Item = Item1 | Item2;
interface Base {
bar: boolean;
}
interface Item1 extends Base {
kind: "A";
foo: string | undefined;
baz: boolean;
qux: true;
}
interface Item2 extends Base {
kind: "B";
foo: string | undefined;
baz: boolean;
qux: false;
}
function goo1(x: Item) {
if (x.kind === "A" && x.foo !== undefined) {
x.foo.length;
}
}
function goo2(x: Item) {
if (x.foo !== undefined && x.kind === "A") {
x.foo.length; // Error, intervening discriminant guard
~~~~~
!!! error TS2532: Object is possibly 'undefined'.
}
}
function foo1(x: Item) {
if (x.bar && x.foo !== undefined) {
x.foo.length;
}
}
function foo2(x: Item) {
if (x.foo !== undefined && x.bar) {
x.foo.length;
}
}
function foo3(x: Item) {
if (x.baz && x.foo !== undefined) {
x.foo.length;
}
}
function foo4(x: Item) {
if (x.foo !== undefined && x.baz) {
x.foo.length;
}
}
function foo5(x: Item) {
if (x.qux && x.foo !== undefined) {
x.foo.length;
}
}
function foo6(x: Item) {
if (x.foo !== undefined && x.qux) {
x.foo.length; // Error, intervening discriminant guard
~~~~~
!!! error TS2532: Object is possibly 'undefined'.
}
}
// Repro from #27493
enum Types { Str = 1, Num = 2 }
type Instance = StrType | NumType;
interface StrType {
type: Types.Str;
value: string;
length: number;
}
interface NumType {
type: Types.Num;
value: number;
}
function func2(inst: Instance) {
while (true) {
switch (inst.type) {
case Types.Str: {
inst.value.length;
break;
}
case Types.Num: {
inst.value.toExponential;
break;
}
}
}
}
// Repro from #29106
const f = (_a: string, _b: string): void => {};
interface A {
a?: string;
b?: string;
}
interface B {
a: string;
b: string;
}
type U = A | B;
const u: U = {} as any;
u.a && u.b && f(u.a, u.b);
u.b && u.a && f(u.a, u.b);
// Repro from #29012
type Additive = '+' | '-';
type Multiplicative = '*' | '/';
interface AdditiveObj {
key: Additive
}
interface MultiplicativeObj {
key: Multiplicative
}
type Obj = AdditiveObj | MultiplicativeObj
export function foo(obj: Obj) {
switch (obj.key) {
case '+': {
onlyPlus(obj.key);
return;
}
}
}
function onlyPlus(arg: '+') {
return arg;
}
// Repro from #29496
declare function never(value: never): never;
const enum BarEnum {
bar1 = 1,
bar2 = 2,
}
type UnionOfBar = TypeBar1 | TypeBar2;
type TypeBar1 = { type: BarEnum.bar1 };
type TypeBar2 = { type: BarEnum.bar2 };
function func3(value: Partial<UnionOfBar>) {
if (value.type !== undefined) {
switch (value.type) {
case BarEnum.bar1:
break;
case BarEnum.bar2:
break;
default:
never(value.type);
}
}
}

View File

@ -82,9 +82,9 @@ function goo2(x: Item) {
x.foo.length; // Error, intervening discriminant guard
>x.foo.length : number
>x.foo : string | undefined
>x.foo : string
>x : Item1
>foo : string | undefined
>foo : string
>length : number
}
}
@ -226,9 +226,9 @@ function foo6(x: Item) {
x.foo.length; // Error, intervening discriminant guard
>x.foo.length : number
>x.foo : string | undefined
>x.foo : string
>x : Item1
>foo : string | undefined
>foo : string
>length : number
}
}