diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b1f516dad69d9..3588af42f2e13 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -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(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(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, 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, 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, expr, t => getTypeWithFacts(t, assumePresent ? TypeFacts.NEUndefinedOrNull : TypeFacts.EQUndefinedOrNull)); } - if (containsMatchingReferenceDiscriminant(reference, expr)) { - return declaredType; - } return type; } } diff --git a/tests/baselines/reference/discriminantPropertyCheck.errors.txt b/tests/baselines/reference/discriminantPropertyCheck.errors.txt deleted file mode 100644 index 962faaa740ebc..0000000000000 --- a/tests/baselines/reference/discriminantPropertyCheck.errors.txt +++ /dev/null @@ -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) { - if (value.type !== undefined) { - switch (value.type) { - case BarEnum.bar1: - break; - case BarEnum.bar2: - break; - default: - never(value.type); - } - } - } - \ No newline at end of file diff --git a/tests/baselines/reference/discriminantPropertyCheck.types b/tests/baselines/reference/discriminantPropertyCheck.types index def89e7be1ffc..5f492087e4da1 100644 --- a/tests/baselines/reference/discriminantPropertyCheck.types +++ b/tests/baselines/reference/discriminantPropertyCheck.types @@ -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 } }