mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-06 02:33:53 -06:00
Remove CFA discriminant check restrictions (#36114)
* Remove unnecessary containsMatchingReferenceDiscriminant logic * Accept new baselines
This commit is contained in:
parent
79dcd3dba1
commit
94271aa753
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user