Skip to content

Commit a612d58

Browse files
authored
Merge pull request #10582 from oijazsh/TS2339
Show an elaboration when accessing a non-existent property of a union type
2 parents 5721f03 + f825b9c commit a612d58

7 files changed

+219
-2
lines changed

src/compiler/checker.ts

+15-1
Original file line numberDiff line numberDiff line change
@@ -10869,7 +10869,7 @@ namespace ts {
1086910869
const prop = getPropertyOfType(apparentType, right.text);
1087010870
if (!prop) {
1087110871
if (right.text && !checkAndReportErrorForExtendingInterface(node)) {
10872-
error(right, Diagnostics.Property_0_does_not_exist_on_type_1, declarationNameToString(right), typeToString(type.flags & TypeFlags.ThisType ? apparentType : type));
10872+
reportNonexistentProperty(right, type.flags & TypeFlags.ThisType ? apparentType : type);
1087310873
}
1087410874
return unknownType;
1087510875
}
@@ -10903,6 +10903,20 @@ namespace ts {
1090310903
return propType;
1090410904
}
1090510905
return getFlowTypeOfReference(node, propType, /*assumeInitialized*/ true, /*flowContainer*/ undefined);
10906+
10907+
function reportNonexistentProperty(propNode: Identifier, containingType: Type) {
10908+
let errorInfo: DiagnosticMessageChain;
10909+
if (containingType.flags & TypeFlags.Union && !(containingType.flags & TypeFlags.Primitive)) {
10910+
for (const subtype of (containingType as UnionType).types) {
10911+
if (!getPropertyOfType(subtype, propNode.text)) {
10912+
errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1, declarationNameToString(propNode), typeToString(subtype));
10913+
break;
10914+
}
10915+
}
10916+
}
10917+
errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1, declarationNameToString(propNode), typeToString(containingType));
10918+
diagnostics.add(createDiagnosticForNodeFromMessageChain(propNode, errorInfo));
10919+
}
1090610920
}
1090710921

1090810922
function isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: string): boolean {

tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.errors.txt

+12
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,20 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru
55
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(41,10): error TS2339: Property 'bar' does not exist on type 'B<any>'.
66
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(66,10): error TS2339: Property 'bar2' does not exist on type 'C1'.
77
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(72,10): error TS2339: Property 'bar1' does not exist on type 'C1 | C2'.
8+
Property 'bar1' does not exist on type 'C2'.
89
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(73,10): error TS2339: Property 'bar2' does not exist on type 'C1 | C2'.
10+
Property 'bar2' does not exist on type 'C1'.
911
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(85,10): error TS2339: Property 'bar' does not exist on type 'D'.
1012
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(91,10): error TS2339: Property 'bar' does not exist on type 'D'.
1113
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(112,10): error TS2339: Property 'bar2' does not exist on type 'E1'.
1214
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(118,11): error TS2339: Property 'bar1' does not exist on type 'E1 | E2'.
15+
Property 'bar1' does not exist on type 'E2'.
1316
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(119,11): error TS2339: Property 'bar2' does not exist on type 'E1 | E2'.
17+
Property 'bar2' does not exist on type 'E1'.
1418
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(134,11): error TS2339: Property 'foo' does not exist on type 'string | F'.
19+
Property 'foo' does not exist on type 'string'.
1520
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(135,11): error TS2339: Property 'bar' does not exist on type 'string | F'.
21+
Property 'bar' does not exist on type 'string'.
1622
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(160,11): error TS2339: Property 'foo2' does not exist on type 'G1'.
1723
tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(166,11): error TS2339: Property 'foo2' does not exist on type 'G1'.
1824
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
107113
obj6.bar1;
108114
~~~~
109115
!!! error TS2339: Property 'bar1' does not exist on type 'C1 | C2'.
116+
!!! error TS2339: Property 'bar1' does not exist on type 'C2'.
110117
obj6.bar2;
111118
~~~~
112119
!!! error TS2339: Property 'bar2' does not exist on type 'C1 | C2'.
120+
!!! error TS2339: Property 'bar2' does not exist on type 'C1'.
113121
}
114122

115123
// with object type literal
@@ -163,9 +171,11 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru
163171
obj10.bar1;
164172
~~~~
165173
!!! error TS2339: Property 'bar1' does not exist on type 'E1 | E2'.
174+
!!! error TS2339: Property 'bar1' does not exist on type 'E2'.
166175
obj10.bar2;
167176
~~~~
168177
!!! error TS2339: Property 'bar2' does not exist on type 'E1 | E2'.
178+
!!! error TS2339: Property 'bar2' does not exist on type 'E1'.
169179
}
170180

171181
// a construct signature that returns any
@@ -183,9 +193,11 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru
183193
obj11.foo;
184194
~~~
185195
!!! error TS2339: Property 'foo' does not exist on type 'string | F'.
196+
!!! error TS2339: Property 'foo' does not exist on type 'string'.
186197
obj11.bar;
187198
~~~
188199
!!! error TS2339: Property 'bar' does not exist on type 'string | F'.
200+
!!! error TS2339: Property 'bar' does not exist on type 'string'.
189201
}
190202

191203
var obj12: any;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
tests/cases/compiler/unionPropertyExistence.ts(27,3): error TS2339: Property 'nope' does not exist on type '"foo" | "bar"'.
2+
Property 'nope' does not exist on type '"foo"'.
3+
tests/cases/compiler/unionPropertyExistence.ts(28,6): error TS2339: Property 'onlyInB' does not exist on type 'B | "foo"'.
4+
Property 'onlyInB' does not exist on type '"foo"'.
5+
tests/cases/compiler/unionPropertyExistence.ts(30,6): error TS2339: Property 'length' does not exist on type 'B | "foo"'.
6+
Property 'length' does not exist on type 'B'.
7+
tests/cases/compiler/unionPropertyExistence.ts(32,4): error TS2339: Property 'onlyInB' does not exist on type 'AB'.
8+
Property 'onlyInB' does not exist on type 'A'.
9+
tests/cases/compiler/unionPropertyExistence.ts(35,5): error TS2339: Property 'notInC' does not exist on type 'ABC'.
10+
Property 'notInC' does not exist on type 'C'.
11+
tests/cases/compiler/unionPropertyExistence.ts(36,4): error TS2339: Property 'notInB' does not exist on type 'AB'.
12+
Property 'notInB' does not exist on type 'B'.
13+
tests/cases/compiler/unionPropertyExistence.ts(37,5): error TS2339: Property 'notInB' does not exist on type 'ABC'.
14+
Property 'notInB' does not exist on type 'B'.
15+
tests/cases/compiler/unionPropertyExistence.ts(40,5): error TS2339: Property 'inNone' does not exist on type 'ABC'.
16+
Property 'inNone' does not exist on type 'A'.
17+
18+
19+
==== tests/cases/compiler/unionPropertyExistence.ts (8 errors) ====
20+
interface A {
21+
inAll: string;
22+
notInB: string;
23+
notInC: string;
24+
}
25+
26+
interface B {
27+
inAll: boolean;
28+
onlyInB: number;
29+
notInC: string;
30+
}
31+
32+
interface C {
33+
inAll: number;
34+
notInB: string;
35+
}
36+
37+
type AB = A | B;
38+
type ABC = C | AB;
39+
40+
var ab: AB;
41+
var abc: ABC;
42+
43+
declare const x: "foo" | "bar";
44+
declare const bFoo: B | "foo";
45+
46+
x.nope();
47+
~~~~
48+
!!! error TS2339: Property 'nope' does not exist on type '"foo" | "bar"'.
49+
!!! error TS2339: Property 'nope' does not exist on type '"foo"'.
50+
bFoo.onlyInB;
51+
~~~~~~~
52+
!!! error TS2339: Property 'onlyInB' does not exist on type 'B | "foo"'.
53+
!!! error TS2339: Property 'onlyInB' does not exist on type '"foo"'.
54+
x.length; // Ok
55+
bFoo.length;
56+
~~~~~~
57+
!!! error TS2339: Property 'length' does not exist on type 'B | "foo"'.
58+
!!! error TS2339: Property 'length' does not exist on type 'B'.
59+
60+
ab.onlyInB;
61+
~~~~~~~
62+
!!! error TS2339: Property 'onlyInB' does not exist on type 'AB'.
63+
!!! error TS2339: Property 'onlyInB' does not exist on type 'A'.
64+
65+
ab.notInC; // Ok
66+
abc.notInC;
67+
~~~~~~
68+
!!! error TS2339: Property 'notInC' does not exist on type 'ABC'.
69+
!!! error TS2339: Property 'notInC' does not exist on type 'C'.
70+
ab.notInB;
71+
~~~~~~
72+
!!! error TS2339: Property 'notInB' does not exist on type 'AB'.
73+
!!! error TS2339: Property 'notInB' does not exist on type 'B'.
74+
abc.notInB;
75+
~~~~~~
76+
!!! error TS2339: Property 'notInB' does not exist on type 'ABC'.
77+
!!! error TS2339: Property 'notInB' does not exist on type 'B'.
78+
79+
abc.inAll; // Ok
80+
abc.inNone;
81+
~~~~~~
82+
!!! error TS2339: Property 'inNone' does not exist on type 'ABC'.
83+
!!! error TS2339: Property 'inNone' does not exist on type 'A'.
84+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
//// [unionPropertyExistence.ts]
2+
interface A {
3+
inAll: string;
4+
notInB: string;
5+
notInC: string;
6+
}
7+
8+
interface B {
9+
inAll: boolean;
10+
onlyInB: number;
11+
notInC: string;
12+
}
13+
14+
interface C {
15+
inAll: number;
16+
notInB: string;
17+
}
18+
19+
type AB = A | B;
20+
type ABC = C | AB;
21+
22+
var ab: AB;
23+
var abc: ABC;
24+
25+
declare const x: "foo" | "bar";
26+
declare const bFoo: B | "foo";
27+
28+
x.nope();
29+
bFoo.onlyInB;
30+
x.length; // Ok
31+
bFoo.length;
32+
33+
ab.onlyInB;
34+
35+
ab.notInC; // Ok
36+
abc.notInC;
37+
ab.notInB;
38+
abc.notInB;
39+
40+
abc.inAll; // Ok
41+
abc.inNone;
42+
43+
44+
//// [unionPropertyExistence.js]
45+
var ab;
46+
var abc;
47+
x.nope();
48+
bFoo.onlyInB;
49+
x.length; // Ok
50+
bFoo.length;
51+
ab.onlyInB;
52+
ab.notInC; // Ok
53+
abc.notInC;
54+
ab.notInB;
55+
abc.notInB;
56+
abc.inAll; // Ok
57+
abc.inNone;

tests/baselines/reference/unionTypeMembers.errors.txt

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
tests/cases/conformance/types/union/unionTypeMembers.ts(44,1): error TS2349: Cannot invoke an expression whose type lacks a call signature.
22
tests/cases/conformance/types/union/unionTypeMembers.ts(51,3): error TS2339: Property 'propertyOnlyInI1' does not exist on type 'I1<number> | I2<number>'.
3+
Property 'propertyOnlyInI1' does not exist on type 'I2<number>'.
34
tests/cases/conformance/types/union/unionTypeMembers.ts(52,3): error TS2339: Property 'propertyOnlyInI2' does not exist on type 'I1<number> | I2<number>'.
5+
Property 'propertyOnlyInI2' does not exist on type 'I1<number>'.
46
tests/cases/conformance/types/union/unionTypeMembers.ts(53,3): error TS2339: Property 'methodOnlyInI1' does not exist on type 'I1<number> | I2<number>'.
7+
Property 'methodOnlyInI1' does not exist on type 'I2<number>'.
58
tests/cases/conformance/types/union/unionTypeMembers.ts(54,3): error TS2339: Property 'methodOnlyInI2' does not exist on type 'I1<number> | I2<number>'.
9+
Property 'methodOnlyInI2' does not exist on type 'I1<number>'.
610

711

812
==== 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
6165
x.propertyOnlyInI1; // error
6266
~~~~~~~~~~~~~~~~
6367
!!! error TS2339: Property 'propertyOnlyInI1' does not exist on type 'I1<number> | I2<number>'.
68+
!!! error TS2339: Property 'propertyOnlyInI1' does not exist on type 'I2<number>'.
6469
x.propertyOnlyInI2; // error
6570
~~~~~~~~~~~~~~~~
6671
!!! error TS2339: Property 'propertyOnlyInI2' does not exist on type 'I1<number> | I2<number>'.
72+
!!! error TS2339: Property 'propertyOnlyInI2' does not exist on type 'I1<number>'.
6773
x.methodOnlyInI1("hello"); // error
6874
~~~~~~~~~~~~~~
6975
!!! error TS2339: Property 'methodOnlyInI1' does not exist on type 'I1<number> | I2<number>'.
76+
!!! error TS2339: Property 'methodOnlyInI1' does not exist on type 'I2<number>'.
7077
x.methodOnlyInI2(10); // error
7178
~~~~~~~~~~~~~~
72-
!!! error TS2339: Property 'methodOnlyInI2' does not exist on type 'I1<number> | I2<number>'.
79+
!!! error TS2339: Property 'methodOnlyInI2' does not exist on type 'I1<number> | I2<number>'.
80+
!!! error TS2339: Property 'methodOnlyInI2' does not exist on type 'I1<number>'.

tests/baselines/reference/unionTypeReadonly.errors.txt

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ tests/cases/conformance/types/union/unionTypeReadonly.ts(19,1): error TS2450: Le
33
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.
44
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.
55
tests/cases/conformance/types/union/unionTypeReadonly.ts(25,15): error TS2339: Property 'value' does not exist on type 'Base | DifferentName'.
6+
Property 'value' does not exist on type 'DifferentName'.
67

78

89
==== 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
4142
differentName.value = 12; // error, property 'value' doesn't exist
4243
~~~~~
4344
!!! error TS2339: Property 'value' does not exist on type 'Base | DifferentName'.
45+
!!! error TS2339: Property 'value' does not exist on type 'DifferentName'.
4446

4547

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
interface A {
2+
inAll: string;
3+
notInB: string;
4+
notInC: string;
5+
}
6+
7+
interface B {
8+
inAll: boolean;
9+
onlyInB: number;
10+
notInC: string;
11+
}
12+
13+
interface C {
14+
inAll: number;
15+
notInB: string;
16+
}
17+
18+
type AB = A | B;
19+
type ABC = C | AB;
20+
21+
var ab: AB;
22+
var abc: ABC;
23+
24+
declare const x: "foo" | "bar";
25+
declare const bFoo: B | "foo";
26+
27+
x.nope();
28+
bFoo.onlyInB;
29+
x.length; // Ok
30+
bFoo.length;
31+
32+
ab.onlyInB;
33+
34+
ab.notInC; // Ok
35+
abc.notInC;
36+
ab.notInB;
37+
abc.notInB;
38+
39+
abc.inAll; // Ok
40+
abc.inNone;

0 commit comments

Comments
 (0)