Skip to content

Commit 167258b

Browse files
Andaristsnovader
authored andcommitted
Fixed element access expression writes for divergent write types (microsoft#55585)
1 parent 64d643d commit 167258b

15 files changed

+1182
-12
lines changed

src/compiler/checker.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -686,6 +686,7 @@ import {
686686
isRequireCall,
687687
isRestParameter,
688688
isRestTypeNode,
689+
isRightSideOfAccessExpression,
689690
isRightSideOfQualifiedNameOrPropertyAccess,
690691
isRightSideOfQualifiedNameOrPropertyAccessOrJSDocMemberName,
691692
isSameEntityName,
@@ -17737,7 +17738,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1773717738
return autoType;
1773817739
}
1773917740
}
17740-
const propType = getTypeOfSymbol(prop);
17741+
const propType = accessFlags & AccessFlags.Writing ? getWriteTypeOfSymbol(prop) : getTypeOfSymbol(prop);
1774117742
return accessExpression && getAssignmentTargetKind(accessExpression) !== AssignmentKind.Definite ? getFlowTypeOfReference(accessExpression, propType) :
1774217743
accessNode && isIndexedAccessTypeNode(accessNode) && containsMissingType(propType) ? getUnionType([propType, undefinedType]) :
1774317744
propType;
@@ -28229,7 +28230,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2822928230
// to it at the given location. Since we have no control flow information for the
2823028231
// hypothetical reference (control flow information is created and attached by the
2823128232
// binder), we simply return the declared type of the symbol.
28232-
return getNonMissingTypeOfSymbol(symbol);
28233+
return isRightSideOfAccessExpression(location) && isWriteAccess(location.parent) ? getWriteTypeOfSymbol(symbol) : getNonMissingTypeOfSymbol(symbol);
2823328234
}
2823428235

2823528236
function getControlFlowContainer(node: Node): Node {

src/compiler/utilities.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7259,8 +7259,8 @@ export function isRightSideOfQualifiedNameOrPropertyAccess(node: Node) {
72597259

72607260
/** @internal */
72617261
export function isRightSideOfAccessExpression(node: Node) {
7262-
return isPropertyAccessExpression(node.parent) && node.parent.name === node
7263-
|| isElementAccessExpression(node.parent) && node.parent.argumentExpression === node;
7262+
return !!node.parent && (isPropertyAccessExpression(node.parent) && node.parent.name === node
7263+
|| isElementAccessExpression(node.parent) && node.parent.argumentExpression === node);
72647264
}
72657265

72667266
/** @internal */

tests/baselines/reference/divergentAccessorsTypes7.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ class Test<S> {
99
get value(): string {
1010
return null!;
1111
}
12-
12+
1313
// -- Replacing the getter such that the getter/setter types match, removes the error:
1414
// get value(): string | ((item: S) => string) {
1515
// return null!;
1616
// }
17-
17+
1818
// -- Or, replacing the setter such that a concrete type is used, removes the error:
1919
// set value(value: string | ((item: { property: string }) => string)) {}
2020
}
@@ -24,6 +24,7 @@ const a = new Test<{
2424
}>();
2525

2626
a.value = (item) => item.property
27+
a['value'] = (item) => item.property
2728

2829

2930
//// [divergentAccessorsTypes7.js]
@@ -42,3 +43,4 @@ var Test = /** @class */ (function () {
4243
}());
4344
var a = new Test();
4445
a.value = function (item) { return item.property; };
46+
a['value'] = function (item) { return item.property; };

tests/baselines/reference/divergentAccessorsTypes7.symbols

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ class Test<S> {
1818

1919
return null!;
2020
}
21-
21+
2222
// -- Replacing the getter such that the getter/setter types match, removes the error:
2323
// get value(): string | ((item: S) => string) {
2424
// return null!;
2525
// }
26-
26+
2727
// -- Or, replacing the setter such that a concrete type is used, removes the error:
2828
// set value(value: string | ((item: { property: string }) => string)) {}
2929
}
@@ -46,3 +46,11 @@ a.value = (item) => item.property
4646
>item : Symbol(item, Decl(divergentAccessorsTypes7.ts, 22, 11))
4747
>property : Symbol(property, Decl(divergentAccessorsTypes7.ts, 18, 20))
4848

49+
a['value'] = (item) => item.property
50+
>a : Symbol(a, Decl(divergentAccessorsTypes7.ts, 18, 5))
51+
>'value' : Symbol(Test.value, Decl(divergentAccessorsTypes7.ts, 1, 20), Decl(divergentAccessorsTypes7.ts, 3, 55))
52+
>item : Symbol(item, Decl(divergentAccessorsTypes7.ts, 23, 14))
53+
>item.property : Symbol(property, Decl(divergentAccessorsTypes7.ts, 18, 20))
54+
>item : Symbol(item, Decl(divergentAccessorsTypes7.ts, 23, 14))
55+
>property : Symbol(property, Decl(divergentAccessorsTypes7.ts, 18, 20))
56+

tests/baselines/reference/divergentAccessorsTypes7.types

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@ class Test<S> {
1717
return null!;
1818
>null! : null
1919
}
20-
20+
2121
// -- Replacing the getter such that the getter/setter types match, removes the error:
2222
// get value(): string | ((item: S) => string) {
2323
// return null!;
2424
// }
25-
25+
2626
// -- Or, replacing the setter such that a concrete type is used, removes the error:
2727
// set value(value: string | ((item: { property: string }) => string)) {}
2828
}
@@ -48,3 +48,14 @@ a.value = (item) => item.property
4848
>item : { property: string; }
4949
>property : string
5050

51+
a['value'] = (item) => item.property
52+
>a['value'] = (item) => item.property : (item: { property: string; }) => string
53+
>a['value'] : string | ((item: { property: string; }) => string)
54+
>a : Test<{ property: string; }>
55+
>'value' : "value"
56+
>(item) => item.property : (item: { property: string; }) => string
57+
>item : { property: string; }
58+
>item.property : string
59+
>item : { property: string; }
60+
>property : string
61+
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
divergentAccessorsTypes8.ts(20,1): error TS2322: Type 'CSSStyleDeclaration' is not assignable to type 'string'.
2+
divergentAccessorsTypes8.ts(86,1): error TS2322: Type 'number' is not assignable to type 'string'.
3+
divergentAccessorsTypes8.ts(124,1): error TS2322: Type '42' is not assignable to type '"hello"'.
4+
divergentAccessorsTypes8.ts(128,1): error TS2322: Type '"hello"' is not assignable to type '42'.
5+
divergentAccessorsTypes8.ts(146,1): error TS2322: Type 'number' is not assignable to type 'boolean'.
6+
divergentAccessorsTypes8.ts(148,1): error TS2322: Type 'string' is not assignable to type 'boolean'.
7+
divergentAccessorsTypes8.ts(149,1): error TS2322: Type 'null' is not assignable to type 'boolean'.
8+
9+
10+
==== divergentAccessorsTypes8.ts (7 errors) ====
11+
export {}
12+
13+
interface Serializer {
14+
set value(v: string | number | boolean);
15+
get value(): string;
16+
}
17+
declare let box: Serializer;
18+
const v = box['value']
19+
box['value'] = true;
20+
box['value'] = 42;
21+
box['value'] = "hello";
22+
23+
interface Element {
24+
get style(): CSSStyleDeclaration;
25+
set style(cssText: string);
26+
}
27+
28+
declare const element: Element;
29+
element['style'] = "color: red";
30+
element['style'] = element.style;
31+
~~~~~~~~~~~~~~~~
32+
!!! error TS2322: Type 'CSSStyleDeclaration' is not assignable to type 'string'.
33+
34+
class One {
35+
get prop1(): string {
36+
return "";
37+
}
38+
set prop1(s: string | number) {}
39+
40+
get prop2(): string {
41+
return "";
42+
}
43+
set prop2(s: string | number) {}
44+
45+
prop3: number = 42;
46+
47+
get prop4(): string {
48+
return "";
49+
}
50+
set prop4(s: string | number) {}
51+
}
52+
53+
class Two {
54+
get prop1(): string {
55+
return "";
56+
}
57+
set prop1(s: string | number) {}
58+
59+
get prop2(): string {
60+
return "";
61+
}
62+
set prop2(s: string) {}
63+
64+
get prop3(): string {
65+
return "";
66+
}
67+
set prop3(s: string | boolean) {}
68+
69+
get prop4(): string {
70+
return "";
71+
}
72+
set prop4(s: string | boolean) {}
73+
}
74+
75+
declare const u1: One | Two;
76+
77+
u1['prop1'] = 42;
78+
u1['prop1'] = "hello";
79+
80+
u1['prop2'] = 42;
81+
u1['prop2'] = "hello";
82+
83+
u1['prop3'] = 42;
84+
u1['prop3'] = "hello";
85+
u1['prop3'] = true;
86+
87+
u1['prop4'] = 42;
88+
u1['prop4'] = "hello";
89+
u1['prop4'] = true;
90+
91+
declare const i: One & Two;
92+
93+
const iv1 = i['prop1'];
94+
i['prop1'] = 42;
95+
i['prop1'] = "hello";
96+
97+
const iv2 = i['prop2'];
98+
i['prop2'] = 42;
99+
~~~~~~~~~~
100+
!!! error TS2322: Type 'number' is not assignable to type 'string'.
101+
i['prop2'] = "hello";
102+
103+
class Three {
104+
get prop1(): string {
105+
return "";
106+
}
107+
set prop1(s: string | number) {}
108+
109+
prop2: number = 42;
110+
}
111+
112+
class Four {
113+
get prop1(): "hello" {
114+
return "hello";
115+
}
116+
set prop1(s: "hello" | number) {}
117+
118+
get prop2(): string {
119+
return "";
120+
}
121+
set prop2(s: string | 42) {}
122+
}
123+
124+
class Five {
125+
get prop1(): "hello" {
126+
return "hello";
127+
}
128+
set prop1(s: "hello" | boolean) {}
129+
130+
get prop2(): string {
131+
return "";
132+
}
133+
set prop2(s: string | number | boolean) {}
134+
}
135+
136+
declare const i2: Three & Four & Five;
137+
138+
i2['prop1'] = 42;
139+
~~~~~~~~~~~
140+
!!! error TS2322: Type '42' is not assignable to type '"hello"'.
141+
i2['prop1'] = "hello";
142+
143+
i2['prop2'] = 42;
144+
i2['prop2'] = "hello";
145+
~~~~~~~~~~~
146+
!!! error TS2322: Type '"hello"' is not assignable to type '42'.
147+
148+
class Six {
149+
get prop1(): boolean | number {
150+
return 42;
151+
}
152+
set prop1(s: boolean | string) {}
153+
154+
get prop2(): bigint | number {
155+
return 10;
156+
}
157+
set prop2(s: boolean | null) {}
158+
}
159+
160+
declare const s1: Six
161+
declare const k1: 'prop1' | 'prop2'
162+
163+
const sv1 = s1[k1]
164+
s1[k1] = 42
165+
~~~~~~
166+
!!! error TS2322: Type 'number' is not assignable to type 'boolean'.
167+
s1[k1] = true
168+
s1[k1] = ''
169+
~~~~~~
170+
!!! error TS2322: Type 'string' is not assignable to type 'boolean'.
171+
s1[k1] = null
172+
~~~~~~
173+
!!! error TS2322: Type 'null' is not assignable to type 'boolean'.
174+

0 commit comments

Comments
 (0)