mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-05 08:11:30 -06:00
Merge pull request #10582 from oijazsh/TS2339
Show an elaboration when accessing a non-existent property of a union type
This commit is contained in:
commit
a612d586c6
@ -10869,7 +10869,7 @@ namespace ts {
|
||||
const prop = getPropertyOfType(apparentType, right.text);
|
||||
if (!prop) {
|
||||
if (right.text && !checkAndReportErrorForExtendingInterface(node)) {
|
||||
error(right, Diagnostics.Property_0_does_not_exist_on_type_1, declarationNameToString(right), typeToString(type.flags & TypeFlags.ThisType ? apparentType : type));
|
||||
reportNonexistentProperty(right, type.flags & TypeFlags.ThisType ? apparentType : type);
|
||||
}
|
||||
return unknownType;
|
||||
}
|
||||
@ -10903,6 +10903,20 @@ namespace ts {
|
||||
return propType;
|
||||
}
|
||||
return getFlowTypeOfReference(node, propType, /*assumeInitialized*/ true, /*flowContainer*/ undefined);
|
||||
|
||||
function reportNonexistentProperty(propNode: Identifier, containingType: Type) {
|
||||
let errorInfo: DiagnosticMessageChain;
|
||||
if (containingType.flags & TypeFlags.Union && !(containingType.flags & TypeFlags.Primitive)) {
|
||||
for (const subtype of (containingType as UnionType).types) {
|
||||
if (!getPropertyOfType(subtype, propNode.text)) {
|
||||
errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1, declarationNameToString(propNode), typeToString(subtype));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1, declarationNameToString(propNode), typeToString(containingType));
|
||||
diagnostics.add(createDiagnosticForNodeFromMessageChain(propNode, errorInfo));
|
||||
}
|
||||
}
|
||||
|
||||
function isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: string): boolean {
|
||||
|
||||
@ -5,14 +5,20 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru
|
||||
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(41,10): error TS2339: Property 'bar' does not exist on type 'B<any>'.
|
||||
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(66,10): error TS2339: Property 'bar2' does not exist on type 'C1'.
|
||||
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(72,10): error TS2339: Property 'bar1' does not exist on type 'C1 | C2'.
|
||||
Property 'bar1' does not exist on type 'C2'.
|
||||
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(73,10): error TS2339: Property 'bar2' does not exist on type 'C1 | C2'.
|
||||
Property 'bar2' does not exist on type 'C1'.
|
||||
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(85,10): error TS2339: Property 'bar' does not exist on type 'D'.
|
||||
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(91,10): error TS2339: Property 'bar' does not exist on type 'D'.
|
||||
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(112,10): error TS2339: Property 'bar2' does not exist on type 'E1'.
|
||||
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(118,11): error TS2339: Property 'bar1' does not exist on type 'E1 | E2'.
|
||||
Property 'bar1' does not exist on type 'E2'.
|
||||
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(119,11): error TS2339: Property 'bar2' does not exist on type 'E1 | E2'.
|
||||
Property 'bar2' does not exist on type 'E1'.
|
||||
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(134,11): error TS2339: Property 'foo' does not exist on type 'string | F'.
|
||||
Property 'foo' does not exist on type 'string'.
|
||||
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(135,11): error TS2339: Property 'bar' does not exist on type 'string | F'.
|
||||
Property 'bar' does not exist on type 'string'.
|
||||
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(160,11): error TS2339: Property 'foo2' does not exist on type 'G1'.
|
||||
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(166,11): error TS2339: Property 'foo2' does not exist on type 'G1'.
|
||||
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(182,11): error TS2339: Property 'bar' does not exist on type 'H'.
|
||||
@ -107,9 +113,11 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru
|
||||
obj6.bar1;
|
||||
~~~~
|
||||
!!! error TS2339: Property 'bar1' does not exist on type 'C1 | C2'.
|
||||
!!! error TS2339: Property 'bar1' does not exist on type 'C2'.
|
||||
obj6.bar2;
|
||||
~~~~
|
||||
!!! error TS2339: Property 'bar2' does not exist on type 'C1 | C2'.
|
||||
!!! error TS2339: Property 'bar2' does not exist on type 'C1'.
|
||||
}
|
||||
|
||||
// with object type literal
|
||||
@ -163,9 +171,11 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru
|
||||
obj10.bar1;
|
||||
~~~~
|
||||
!!! error TS2339: Property 'bar1' does not exist on type 'E1 | E2'.
|
||||
!!! error TS2339: Property 'bar1' does not exist on type 'E2'.
|
||||
obj10.bar2;
|
||||
~~~~
|
||||
!!! error TS2339: Property 'bar2' does not exist on type 'E1 | E2'.
|
||||
!!! error TS2339: Property 'bar2' does not exist on type 'E1'.
|
||||
}
|
||||
|
||||
// a construct signature that returns any
|
||||
@ -183,9 +193,11 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru
|
||||
obj11.foo;
|
||||
~~~
|
||||
!!! error TS2339: Property 'foo' does not exist on type 'string | F'.
|
||||
!!! error TS2339: Property 'foo' does not exist on type 'string'.
|
||||
obj11.bar;
|
||||
~~~
|
||||
!!! error TS2339: Property 'bar' does not exist on type 'string | F'.
|
||||
!!! error TS2339: Property 'bar' does not exist on type 'string'.
|
||||
}
|
||||
|
||||
var obj12: any;
|
||||
|
||||
84
tests/baselines/reference/unionPropertyExistence.errors.txt
Normal file
84
tests/baselines/reference/unionPropertyExistence.errors.txt
Normal file
@ -0,0 +1,84 @@
|
||||
tests/cases/compiler/unionPropertyExistence.ts(27,3): error TS2339: Property 'nope' does not exist on type '"foo" | "bar"'.
|
||||
Property 'nope' does not exist on type '"foo"'.
|
||||
tests/cases/compiler/unionPropertyExistence.ts(28,6): error TS2339: Property 'onlyInB' does not exist on type 'B | "foo"'.
|
||||
Property 'onlyInB' does not exist on type '"foo"'.
|
||||
tests/cases/compiler/unionPropertyExistence.ts(30,6): error TS2339: Property 'length' does not exist on type 'B | "foo"'.
|
||||
Property 'length' does not exist on type 'B'.
|
||||
tests/cases/compiler/unionPropertyExistence.ts(32,4): error TS2339: Property 'onlyInB' does not exist on type 'AB'.
|
||||
Property 'onlyInB' does not exist on type 'A'.
|
||||
tests/cases/compiler/unionPropertyExistence.ts(35,5): error TS2339: Property 'notInC' does not exist on type 'ABC'.
|
||||
Property 'notInC' does not exist on type 'C'.
|
||||
tests/cases/compiler/unionPropertyExistence.ts(36,4): error TS2339: Property 'notInB' does not exist on type 'AB'.
|
||||
Property 'notInB' does not exist on type 'B'.
|
||||
tests/cases/compiler/unionPropertyExistence.ts(37,5): error TS2339: Property 'notInB' does not exist on type 'ABC'.
|
||||
Property 'notInB' does not exist on type 'B'.
|
||||
tests/cases/compiler/unionPropertyExistence.ts(40,5): error TS2339: Property 'inNone' does not exist on type 'ABC'.
|
||||
Property 'inNone' does not exist on type 'A'.
|
||||
|
||||
|
||||
==== tests/cases/compiler/unionPropertyExistence.ts (8 errors) ====
|
||||
interface A {
|
||||
inAll: string;
|
||||
notInB: string;
|
||||
notInC: string;
|
||||
}
|
||||
|
||||
interface B {
|
||||
inAll: boolean;
|
||||
onlyInB: number;
|
||||
notInC: string;
|
||||
}
|
||||
|
||||
interface C {
|
||||
inAll: number;
|
||||
notInB: string;
|
||||
}
|
||||
|
||||
type AB = A | B;
|
||||
type ABC = C | AB;
|
||||
|
||||
var ab: AB;
|
||||
var abc: ABC;
|
||||
|
||||
declare const x: "foo" | "bar";
|
||||
declare const bFoo: B | "foo";
|
||||
|
||||
x.nope();
|
||||
~~~~
|
||||
!!! error TS2339: Property 'nope' does not exist on type '"foo" | "bar"'.
|
||||
!!! error TS2339: Property 'nope' does not exist on type '"foo"'.
|
||||
bFoo.onlyInB;
|
||||
~~~~~~~
|
||||
!!! error TS2339: Property 'onlyInB' does not exist on type 'B | "foo"'.
|
||||
!!! error TS2339: Property 'onlyInB' does not exist on type '"foo"'.
|
||||
x.length; // Ok
|
||||
bFoo.length;
|
||||
~~~~~~
|
||||
!!! error TS2339: Property 'length' does not exist on type 'B | "foo"'.
|
||||
!!! error TS2339: Property 'length' does not exist on type 'B'.
|
||||
|
||||
ab.onlyInB;
|
||||
~~~~~~~
|
||||
!!! error TS2339: Property 'onlyInB' does not exist on type 'AB'.
|
||||
!!! error TS2339: Property 'onlyInB' does not exist on type 'A'.
|
||||
|
||||
ab.notInC; // Ok
|
||||
abc.notInC;
|
||||
~~~~~~
|
||||
!!! error TS2339: Property 'notInC' does not exist on type 'ABC'.
|
||||
!!! error TS2339: Property 'notInC' does not exist on type 'C'.
|
||||
ab.notInB;
|
||||
~~~~~~
|
||||
!!! error TS2339: Property 'notInB' does not exist on type 'AB'.
|
||||
!!! error TS2339: Property 'notInB' does not exist on type 'B'.
|
||||
abc.notInB;
|
||||
~~~~~~
|
||||
!!! error TS2339: Property 'notInB' does not exist on type 'ABC'.
|
||||
!!! error TS2339: Property 'notInB' does not exist on type 'B'.
|
||||
|
||||
abc.inAll; // Ok
|
||||
abc.inNone;
|
||||
~~~~~~
|
||||
!!! error TS2339: Property 'inNone' does not exist on type 'ABC'.
|
||||
!!! error TS2339: Property 'inNone' does not exist on type 'A'.
|
||||
|
||||
57
tests/baselines/reference/unionPropertyExistence.js
Normal file
57
tests/baselines/reference/unionPropertyExistence.js
Normal file
@ -0,0 +1,57 @@
|
||||
//// [unionPropertyExistence.ts]
|
||||
interface A {
|
||||
inAll: string;
|
||||
notInB: string;
|
||||
notInC: string;
|
||||
}
|
||||
|
||||
interface B {
|
||||
inAll: boolean;
|
||||
onlyInB: number;
|
||||
notInC: string;
|
||||
}
|
||||
|
||||
interface C {
|
||||
inAll: number;
|
||||
notInB: string;
|
||||
}
|
||||
|
||||
type AB = A | B;
|
||||
type ABC = C | AB;
|
||||
|
||||
var ab: AB;
|
||||
var abc: ABC;
|
||||
|
||||
declare const x: "foo" | "bar";
|
||||
declare const bFoo: B | "foo";
|
||||
|
||||
x.nope();
|
||||
bFoo.onlyInB;
|
||||
x.length; // Ok
|
||||
bFoo.length;
|
||||
|
||||
ab.onlyInB;
|
||||
|
||||
ab.notInC; // Ok
|
||||
abc.notInC;
|
||||
ab.notInB;
|
||||
abc.notInB;
|
||||
|
||||
abc.inAll; // Ok
|
||||
abc.inNone;
|
||||
|
||||
|
||||
//// [unionPropertyExistence.js]
|
||||
var ab;
|
||||
var abc;
|
||||
x.nope();
|
||||
bFoo.onlyInB;
|
||||
x.length; // Ok
|
||||
bFoo.length;
|
||||
ab.onlyInB;
|
||||
ab.notInC; // Ok
|
||||
abc.notInC;
|
||||
ab.notInB;
|
||||
abc.notInB;
|
||||
abc.inAll; // Ok
|
||||
abc.inNone;
|
||||
@ -1,8 +1,12 @@
|
||||
tests/cases/conformance/types/union/unionTypeMembers.ts(44,1): error TS2349: Cannot invoke an expression whose type lacks a call signature.
|
||||
tests/cases/conformance/types/union/unionTypeMembers.ts(51,3): error TS2339: Property 'propertyOnlyInI1' does not exist on type 'I1<number> | I2<number>'.
|
||||
Property 'propertyOnlyInI1' does not exist on type 'I2<number>'.
|
||||
tests/cases/conformance/types/union/unionTypeMembers.ts(52,3): error TS2339: Property 'propertyOnlyInI2' does not exist on type 'I1<number> | I2<number>'.
|
||||
Property 'propertyOnlyInI2' does not exist on type 'I1<number>'.
|
||||
tests/cases/conformance/types/union/unionTypeMembers.ts(53,3): error TS2339: Property 'methodOnlyInI1' does not exist on type 'I1<number> | I2<number>'.
|
||||
Property 'methodOnlyInI1' does not exist on type 'I2<number>'.
|
||||
tests/cases/conformance/types/union/unionTypeMembers.ts(54,3): error TS2339: Property 'methodOnlyInI2' does not exist on type 'I1<number> | I2<number>'.
|
||||
Property 'methodOnlyInI2' does not exist on type 'I1<number>'.
|
||||
|
||||
|
||||
==== tests/cases/conformance/types/union/unionTypeMembers.ts (5 errors) ====
|
||||
@ -61,12 +65,16 @@ tests/cases/conformance/types/union/unionTypeMembers.ts(54,3): error TS2339: Pro
|
||||
x.propertyOnlyInI1; // error
|
||||
~~~~~~~~~~~~~~~~
|
||||
!!! error TS2339: Property 'propertyOnlyInI1' does not exist on type 'I1<number> | I2<number>'.
|
||||
!!! error TS2339: Property 'propertyOnlyInI1' does not exist on type 'I2<number>'.
|
||||
x.propertyOnlyInI2; // error
|
||||
~~~~~~~~~~~~~~~~
|
||||
!!! error TS2339: Property 'propertyOnlyInI2' does not exist on type 'I1<number> | I2<number>'.
|
||||
!!! error TS2339: Property 'propertyOnlyInI2' does not exist on type 'I1<number>'.
|
||||
x.methodOnlyInI1("hello"); // error
|
||||
~~~~~~~~~~~~~~
|
||||
!!! error TS2339: Property 'methodOnlyInI1' does not exist on type 'I1<number> | I2<number>'.
|
||||
!!! error TS2339: Property 'methodOnlyInI1' does not exist on type 'I2<number>'.
|
||||
x.methodOnlyInI2(10); // error
|
||||
~~~~~~~~~~~~~~
|
||||
!!! error TS2339: Property 'methodOnlyInI2' does not exist on type 'I1<number> | I2<number>'.
|
||||
!!! error TS2339: Property 'methodOnlyInI2' does not exist on type 'I1<number> | I2<number>'.
|
||||
!!! error TS2339: Property 'methodOnlyInI2' does not exist on type 'I1<number>'.
|
||||
@ -3,6 +3,7 @@ tests/cases/conformance/types/union/unionTypeReadonly.ts(19,1): error TS2450: Le
|
||||
tests/cases/conformance/types/union/unionTypeReadonly.ts(21,1): error TS2450: Left-hand side of assignment expression cannot be a constant or a read-only property.
|
||||
tests/cases/conformance/types/union/unionTypeReadonly.ts(23,1): error TS2450: Left-hand side of assignment expression cannot be a constant or a read-only property.
|
||||
tests/cases/conformance/types/union/unionTypeReadonly.ts(25,15): error TS2339: Property 'value' does not exist on type 'Base | DifferentName'.
|
||||
Property 'value' does not exist on type 'DifferentName'.
|
||||
|
||||
|
||||
==== tests/cases/conformance/types/union/unionTypeReadonly.ts (5 errors) ====
|
||||
@ -41,5 +42,6 @@ tests/cases/conformance/types/union/unionTypeReadonly.ts(25,15): error TS2339: P
|
||||
differentName.value = 12; // error, property 'value' doesn't exist
|
||||
~~~~~
|
||||
!!! error TS2339: Property 'value' does not exist on type 'Base | DifferentName'.
|
||||
!!! error TS2339: Property 'value' does not exist on type 'DifferentName'.
|
||||
|
||||
|
||||
40
tests/cases/compiler/unionPropertyExistence.ts
Normal file
40
tests/cases/compiler/unionPropertyExistence.ts
Normal file
@ -0,0 +1,40 @@
|
||||
interface A {
|
||||
inAll: string;
|
||||
notInB: string;
|
||||
notInC: string;
|
||||
}
|
||||
|
||||
interface B {
|
||||
inAll: boolean;
|
||||
onlyInB: number;
|
||||
notInC: string;
|
||||
}
|
||||
|
||||
interface C {
|
||||
inAll: number;
|
||||
notInB: string;
|
||||
}
|
||||
|
||||
type AB = A | B;
|
||||
type ABC = C | AB;
|
||||
|
||||
var ab: AB;
|
||||
var abc: ABC;
|
||||
|
||||
declare const x: "foo" | "bar";
|
||||
declare const bFoo: B | "foo";
|
||||
|
||||
x.nope();
|
||||
bFoo.onlyInB;
|
||||
x.length; // Ok
|
||||
bFoo.length;
|
||||
|
||||
ab.onlyInB;
|
||||
|
||||
ab.notInC; // Ok
|
||||
abc.notInC;
|
||||
ab.notInB;
|
||||
abc.notInB;
|
||||
|
||||
abc.inAll; // Ok
|
||||
abc.inNone;
|
||||
Loading…
x
Reference in New Issue
Block a user