Skip to content

Commit c5b1011

Browse files
authored
Compute writeType from set accessors for union and intersection properties (#47674)
* Compute write type from set accessors for union and intersection properties * Add test for deferred writeType * Always check for writeType of symbol
1 parent 17b97cc commit c5b1011

17 files changed

+1105
-1
lines changed

src/compiler/checker.ts

+45-1
Original file line numberDiff line numberDiff line change
@@ -9696,6 +9696,41 @@ namespace ts {
96969696
return links.type;
96979697
}
96989698

9699+
function getWriteTypeOfSymbolWithDeferredType(symbol: Symbol): Type | undefined {
9700+
const links = getSymbolLinks(symbol);
9701+
if (!links.writeType && links.deferralWriteConstituents) {
9702+
Debug.assertIsDefined(links.deferralParent);
9703+
Debug.assertIsDefined(links.deferralConstituents);
9704+
links.writeType = links.deferralParent.flags & TypeFlags.Union ? getUnionType(links.deferralWriteConstituents) : getIntersectionType(links.deferralWriteConstituents);
9705+
}
9706+
return links.writeType;
9707+
}
9708+
9709+
/**
9710+
* Distinct write types come only from set accessors, but union and intersection
9711+
* properties deriving from set accessors will either pre-compute or defer the
9712+
* union or intersection of the writeTypes of their constituents. To account for
9713+
* this, we will assume that any deferred type or transient symbol may have a
9714+
* `writeType` (or a deferred write type ready to be computed) that should be
9715+
* used before looking for set accessor declarations.
9716+
*/
9717+
function getWriteTypeOfSymbol(symbol: Symbol): Type {
9718+
const checkFlags = getCheckFlags(symbol);
9719+
if (checkFlags & CheckFlags.DeferredType) {
9720+
const writeType = getWriteTypeOfSymbolWithDeferredType(symbol);
9721+
if (writeType) {
9722+
return writeType;
9723+
}
9724+
}
9725+
if (symbol.flags & SymbolFlags.Transient) {
9726+
const { writeType } = symbol as TransientSymbol;
9727+
if (writeType) {
9728+
return writeType;
9729+
}
9730+
}
9731+
return getSetAccessorTypeOfSymbol(symbol);
9732+
}
9733+
96999734
function getSetAccessorTypeOfSymbol(symbol: Symbol): Type {
97009735
if (symbol.flags & SymbolFlags.Accessor) {
97019736
const type = getTypeOfSetAccessor(symbol);
@@ -12198,6 +12233,7 @@ namespace ts {
1219812233
let firstType: Type | undefined;
1219912234
let nameType: Type | undefined;
1220012235
const propTypes: Type[] = [];
12236+
let writeTypes: Type[] | undefined;
1220112237
let firstValueDeclaration: Declaration | undefined;
1220212238
let hasNonUniformValueDeclaration = false;
1220312239
for (const prop of props) {
@@ -12213,6 +12249,10 @@ namespace ts {
1221312249
firstType = type;
1221412250
nameType = getSymbolLinks(prop).nameType;
1221512251
}
12252+
const writeType = getWriteTypeOfSymbol(prop);
12253+
if (writeTypes || writeType !== type) {
12254+
writeTypes = append(!writeTypes ? propTypes.slice() : writeTypes, writeType);
12255+
}
1221612256
else if (type !== firstType) {
1221712257
checkFlags |= CheckFlags.HasNonUniformType;
1221812258
}
@@ -12243,9 +12283,13 @@ namespace ts {
1224312283
result.checkFlags |= CheckFlags.DeferredType;
1224412284
result.deferralParent = containingType;
1224512285
result.deferralConstituents = propTypes;
12286+
result.deferralWriteConstituents = writeTypes;
1224612287
}
1224712288
else {
1224812289
result.type = isUnion ? getUnionType(propTypes) : getIntersectionType(propTypes);
12290+
if (writeTypes) {
12291+
result.writeType = isUnion ? getUnionType(writeTypes) : getIntersectionType(writeTypes);
12292+
}
1224912293
}
1225012294
return result;
1225112295
}
@@ -28509,7 +28553,7 @@ namespace ts {
2850928553
return errorType;
2851028554
}
2851128555

28512-
propType = isThisPropertyAccessInConstructor(node, prop) ? autoType : writing ? getSetAccessorTypeOfSymbol(prop) : getTypeOfSymbol(prop);
28556+
propType = isThisPropertyAccessInConstructor(node, prop) ? autoType : writing ? getWriteTypeOfSymbol(prop) : getTypeOfSymbol(prop);
2851328557
}
2851428558

2851528559
return getFlowTypeOfAccessExpression(node, prop, propType, right, checkMode);

src/compiler/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -4952,6 +4952,7 @@ namespace ts {
49524952
extendedContainersByFile?: ESMap<NodeId, Symbol[]>; // Containers (other than the parent) which this symbol is aliased in
49534953
variances?: VarianceFlags[]; // Alias symbol type argument variance cache
49544954
deferralConstituents?: Type[]; // Calculated list of constituents for a deferred type
4955+
deferralWriteConstituents?: Type[]; // Constituents of a deferred `writeType`
49554956
deferralParent?: Type; // Source union/intersection of a deferred type
49564957
cjsExportMerged?: Symbol; // Version of the symbol with all non export= exports merged with the export= target
49574958
typeOnlyDeclaration?: TypeOnlyAliasDeclaration | false; // First resolved alias declaration that makes the symbol only usable in type constructs

src/compiler/utilities.ts

+4
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ namespace ts {
2020
return undefined;
2121
}
2222

23+
export function getDeclarationsOfKind<T extends Declaration>(symbol: Symbol, kind: T["kind"]): T[] {
24+
return filter(symbol.declarations || emptyArray, d => d.kind === kind) as T[];
25+
}
26+
2327
export function createSymbolTable(symbols?: readonly Symbol[]): SymbolTable {
2428
const result = new Map<__String, Symbol>();
2529
if (symbols) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
//// [divergentAccessorsTypes3.ts]
2+
class One {
3+
get prop1(): string { return ""; }
4+
set prop1(s: string | number) { }
5+
6+
get prop2(): string { return ""; }
7+
set prop2(s: string | number) { }
8+
9+
prop3: number;
10+
11+
get prop4(): string { return ""; }
12+
set prop4(s: string | number) { }
13+
}
14+
15+
class Two {
16+
get prop1(): string { return ""; }
17+
set prop1(s: string | number) { }
18+
19+
get prop2(): string { return ""; }
20+
set prop2(s: string) { }
21+
22+
get prop3(): string { return ""; }
23+
set prop3(s: string | boolean) { }
24+
25+
get prop4(): string { return ""; }
26+
set prop4(s: string | boolean) { }
27+
}
28+
29+
declare const u1: One|Two;
30+
31+
u1.prop1 = 42;
32+
u1.prop1 = "hello";
33+
34+
u1.prop2 = 42;
35+
u1.prop2 = "hello";
36+
37+
u1.prop3 = 42;
38+
u1.prop3 = "hello";
39+
u1.prop3 = true;
40+
41+
u1.prop4 = 42;
42+
u1.prop4 = "hello";
43+
u1.prop4 = true;
44+
45+
46+
//// [divergentAccessorsTypes3.js]
47+
var One = /** @class */ (function () {
48+
function One() {
49+
}
50+
Object.defineProperty(One.prototype, "prop1", {
51+
get: function () { return ""; },
52+
set: function (s) { },
53+
enumerable: false,
54+
configurable: true
55+
});
56+
Object.defineProperty(One.prototype, "prop2", {
57+
get: function () { return ""; },
58+
set: function (s) { },
59+
enumerable: false,
60+
configurable: true
61+
});
62+
Object.defineProperty(One.prototype, "prop4", {
63+
get: function () { return ""; },
64+
set: function (s) { },
65+
enumerable: false,
66+
configurable: true
67+
});
68+
return One;
69+
}());
70+
var Two = /** @class */ (function () {
71+
function Two() {
72+
}
73+
Object.defineProperty(Two.prototype, "prop1", {
74+
get: function () { return ""; },
75+
set: function (s) { },
76+
enumerable: false,
77+
configurable: true
78+
});
79+
Object.defineProperty(Two.prototype, "prop2", {
80+
get: function () { return ""; },
81+
set: function (s) { },
82+
enumerable: false,
83+
configurable: true
84+
});
85+
Object.defineProperty(Two.prototype, "prop3", {
86+
get: function () { return ""; },
87+
set: function (s) { },
88+
enumerable: false,
89+
configurable: true
90+
});
91+
Object.defineProperty(Two.prototype, "prop4", {
92+
get: function () { return ""; },
93+
set: function (s) { },
94+
enumerable: false,
95+
configurable: true
96+
});
97+
return Two;
98+
}());
99+
u1.prop1 = 42;
100+
u1.prop1 = "hello";
101+
u1.prop2 = 42;
102+
u1.prop2 = "hello";
103+
u1.prop3 = 42;
104+
u1.prop3 = "hello";
105+
u1.prop3 = true;
106+
u1.prop4 = 42;
107+
u1.prop4 = "hello";
108+
u1.prop4 = true;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
=== tests/cases/compiler/divergentAccessorsTypes3.ts ===
2+
class One {
3+
>One : Symbol(One, Decl(divergentAccessorsTypes3.ts, 0, 0))
4+
5+
get prop1(): string { return ""; }
6+
>prop1 : Symbol(One.prop1, Decl(divergentAccessorsTypes3.ts, 0, 11), Decl(divergentAccessorsTypes3.ts, 1, 36))
7+
8+
set prop1(s: string | number) { }
9+
>prop1 : Symbol(One.prop1, Decl(divergentAccessorsTypes3.ts, 0, 11), Decl(divergentAccessorsTypes3.ts, 1, 36))
10+
>s : Symbol(s, Decl(divergentAccessorsTypes3.ts, 2, 12))
11+
12+
get prop2(): string { return ""; }
13+
>prop2 : Symbol(One.prop2, Decl(divergentAccessorsTypes3.ts, 2, 35), Decl(divergentAccessorsTypes3.ts, 4, 36))
14+
15+
set prop2(s: string | number) { }
16+
>prop2 : Symbol(One.prop2, Decl(divergentAccessorsTypes3.ts, 2, 35), Decl(divergentAccessorsTypes3.ts, 4, 36))
17+
>s : Symbol(s, Decl(divergentAccessorsTypes3.ts, 5, 12))
18+
19+
prop3: number;
20+
>prop3 : Symbol(One.prop3, Decl(divergentAccessorsTypes3.ts, 5, 35))
21+
22+
get prop4(): string { return ""; }
23+
>prop4 : Symbol(One.prop4, Decl(divergentAccessorsTypes3.ts, 7, 16), Decl(divergentAccessorsTypes3.ts, 9, 36))
24+
25+
set prop4(s: string | number) { }
26+
>prop4 : Symbol(One.prop4, Decl(divergentAccessorsTypes3.ts, 7, 16), Decl(divergentAccessorsTypes3.ts, 9, 36))
27+
>s : Symbol(s, Decl(divergentAccessorsTypes3.ts, 10, 12))
28+
}
29+
30+
class Two {
31+
>Two : Symbol(Two, Decl(divergentAccessorsTypes3.ts, 11, 1))
32+
33+
get prop1(): string { return ""; }
34+
>prop1 : Symbol(Two.prop1, Decl(divergentAccessorsTypes3.ts, 13, 11), Decl(divergentAccessorsTypes3.ts, 14, 36))
35+
36+
set prop1(s: string | number) { }
37+
>prop1 : Symbol(Two.prop1, Decl(divergentAccessorsTypes3.ts, 13, 11), Decl(divergentAccessorsTypes3.ts, 14, 36))
38+
>s : Symbol(s, Decl(divergentAccessorsTypes3.ts, 15, 12))
39+
40+
get prop2(): string { return ""; }
41+
>prop2 : Symbol(Two.prop2, Decl(divergentAccessorsTypes3.ts, 15, 35), Decl(divergentAccessorsTypes3.ts, 17, 36))
42+
43+
set prop2(s: string) { }
44+
>prop2 : Symbol(Two.prop2, Decl(divergentAccessorsTypes3.ts, 15, 35), Decl(divergentAccessorsTypes3.ts, 17, 36))
45+
>s : Symbol(s, Decl(divergentAccessorsTypes3.ts, 18, 12))
46+
47+
get prop3(): string { return ""; }
48+
>prop3 : Symbol(Two.prop3, Decl(divergentAccessorsTypes3.ts, 18, 26), Decl(divergentAccessorsTypes3.ts, 20, 36))
49+
50+
set prop3(s: string | boolean) { }
51+
>prop3 : Symbol(Two.prop3, Decl(divergentAccessorsTypes3.ts, 18, 26), Decl(divergentAccessorsTypes3.ts, 20, 36))
52+
>s : Symbol(s, Decl(divergentAccessorsTypes3.ts, 21, 12))
53+
54+
get prop4(): string { return ""; }
55+
>prop4 : Symbol(Two.prop4, Decl(divergentAccessorsTypes3.ts, 21, 36), Decl(divergentAccessorsTypes3.ts, 23, 36))
56+
57+
set prop4(s: string | boolean) { }
58+
>prop4 : Symbol(Two.prop4, Decl(divergentAccessorsTypes3.ts, 21, 36), Decl(divergentAccessorsTypes3.ts, 23, 36))
59+
>s : Symbol(s, Decl(divergentAccessorsTypes3.ts, 24, 12))
60+
}
61+
62+
declare const u1: One|Two;
63+
>u1 : Symbol(u1, Decl(divergentAccessorsTypes3.ts, 27, 13))
64+
>One : Symbol(One, Decl(divergentAccessorsTypes3.ts, 0, 0))
65+
>Two : Symbol(Two, Decl(divergentAccessorsTypes3.ts, 11, 1))
66+
67+
u1.prop1 = 42;
68+
>u1.prop1 : Symbol(prop1, Decl(divergentAccessorsTypes3.ts, 0, 11), Decl(divergentAccessorsTypes3.ts, 1, 36), Decl(divergentAccessorsTypes3.ts, 13, 11), Decl(divergentAccessorsTypes3.ts, 14, 36))
69+
>u1 : Symbol(u1, Decl(divergentAccessorsTypes3.ts, 27, 13))
70+
>prop1 : Symbol(prop1, Decl(divergentAccessorsTypes3.ts, 0, 11), Decl(divergentAccessorsTypes3.ts, 1, 36), Decl(divergentAccessorsTypes3.ts, 13, 11), Decl(divergentAccessorsTypes3.ts, 14, 36))
71+
72+
u1.prop1 = "hello";
73+
>u1.prop1 : Symbol(prop1, Decl(divergentAccessorsTypes3.ts, 0, 11), Decl(divergentAccessorsTypes3.ts, 1, 36), Decl(divergentAccessorsTypes3.ts, 13, 11), Decl(divergentAccessorsTypes3.ts, 14, 36))
74+
>u1 : Symbol(u1, Decl(divergentAccessorsTypes3.ts, 27, 13))
75+
>prop1 : Symbol(prop1, Decl(divergentAccessorsTypes3.ts, 0, 11), Decl(divergentAccessorsTypes3.ts, 1, 36), Decl(divergentAccessorsTypes3.ts, 13, 11), Decl(divergentAccessorsTypes3.ts, 14, 36))
76+
77+
u1.prop2 = 42;
78+
>u1.prop2 : Symbol(prop2, Decl(divergentAccessorsTypes3.ts, 2, 35), Decl(divergentAccessorsTypes3.ts, 4, 36), Decl(divergentAccessorsTypes3.ts, 15, 35), Decl(divergentAccessorsTypes3.ts, 17, 36))
79+
>u1 : Symbol(u1, Decl(divergentAccessorsTypes3.ts, 27, 13))
80+
>prop2 : Symbol(prop2, Decl(divergentAccessorsTypes3.ts, 2, 35), Decl(divergentAccessorsTypes3.ts, 4, 36), Decl(divergentAccessorsTypes3.ts, 15, 35), Decl(divergentAccessorsTypes3.ts, 17, 36))
81+
82+
u1.prop2 = "hello";
83+
>u1.prop2 : Symbol(prop2, Decl(divergentAccessorsTypes3.ts, 2, 35), Decl(divergentAccessorsTypes3.ts, 4, 36), Decl(divergentAccessorsTypes3.ts, 15, 35), Decl(divergentAccessorsTypes3.ts, 17, 36))
84+
>u1 : Symbol(u1, Decl(divergentAccessorsTypes3.ts, 27, 13))
85+
>prop2 : Symbol(prop2, Decl(divergentAccessorsTypes3.ts, 2, 35), Decl(divergentAccessorsTypes3.ts, 4, 36), Decl(divergentAccessorsTypes3.ts, 15, 35), Decl(divergentAccessorsTypes3.ts, 17, 36))
86+
87+
u1.prop3 = 42;
88+
>u1.prop3 : Symbol(prop3, Decl(divergentAccessorsTypes3.ts, 5, 35), Decl(divergentAccessorsTypes3.ts, 18, 26), Decl(divergentAccessorsTypes3.ts, 20, 36))
89+
>u1 : Symbol(u1, Decl(divergentAccessorsTypes3.ts, 27, 13))
90+
>prop3 : Symbol(prop3, Decl(divergentAccessorsTypes3.ts, 5, 35), Decl(divergentAccessorsTypes3.ts, 18, 26), Decl(divergentAccessorsTypes3.ts, 20, 36))
91+
92+
u1.prop3 = "hello";
93+
>u1.prop3 : Symbol(prop3, Decl(divergentAccessorsTypes3.ts, 5, 35), Decl(divergentAccessorsTypes3.ts, 18, 26), Decl(divergentAccessorsTypes3.ts, 20, 36))
94+
>u1 : Symbol(u1, Decl(divergentAccessorsTypes3.ts, 27, 13))
95+
>prop3 : Symbol(prop3, Decl(divergentAccessorsTypes3.ts, 5, 35), Decl(divergentAccessorsTypes3.ts, 18, 26), Decl(divergentAccessorsTypes3.ts, 20, 36))
96+
97+
u1.prop3 = true;
98+
>u1.prop3 : Symbol(prop3, Decl(divergentAccessorsTypes3.ts, 5, 35), Decl(divergentAccessorsTypes3.ts, 18, 26), Decl(divergentAccessorsTypes3.ts, 20, 36))
99+
>u1 : Symbol(u1, Decl(divergentAccessorsTypes3.ts, 27, 13))
100+
>prop3 : Symbol(prop3, Decl(divergentAccessorsTypes3.ts, 5, 35), Decl(divergentAccessorsTypes3.ts, 18, 26), Decl(divergentAccessorsTypes3.ts, 20, 36))
101+
102+
u1.prop4 = 42;
103+
>u1.prop4 : Symbol(prop4, Decl(divergentAccessorsTypes3.ts, 7, 16), Decl(divergentAccessorsTypes3.ts, 9, 36), Decl(divergentAccessorsTypes3.ts, 21, 36), Decl(divergentAccessorsTypes3.ts, 23, 36))
104+
>u1 : Symbol(u1, Decl(divergentAccessorsTypes3.ts, 27, 13))
105+
>prop4 : Symbol(prop4, Decl(divergentAccessorsTypes3.ts, 7, 16), Decl(divergentAccessorsTypes3.ts, 9, 36), Decl(divergentAccessorsTypes3.ts, 21, 36), Decl(divergentAccessorsTypes3.ts, 23, 36))
106+
107+
u1.prop4 = "hello";
108+
>u1.prop4 : Symbol(prop4, Decl(divergentAccessorsTypes3.ts, 7, 16), Decl(divergentAccessorsTypes3.ts, 9, 36), Decl(divergentAccessorsTypes3.ts, 21, 36), Decl(divergentAccessorsTypes3.ts, 23, 36))
109+
>u1 : Symbol(u1, Decl(divergentAccessorsTypes3.ts, 27, 13))
110+
>prop4 : Symbol(prop4, Decl(divergentAccessorsTypes3.ts, 7, 16), Decl(divergentAccessorsTypes3.ts, 9, 36), Decl(divergentAccessorsTypes3.ts, 21, 36), Decl(divergentAccessorsTypes3.ts, 23, 36))
111+
112+
u1.prop4 = true;
113+
>u1.prop4 : Symbol(prop4, Decl(divergentAccessorsTypes3.ts, 7, 16), Decl(divergentAccessorsTypes3.ts, 9, 36), Decl(divergentAccessorsTypes3.ts, 21, 36), Decl(divergentAccessorsTypes3.ts, 23, 36))
114+
>u1 : Symbol(u1, Decl(divergentAccessorsTypes3.ts, 27, 13))
115+
>prop4 : Symbol(prop4, Decl(divergentAccessorsTypes3.ts, 7, 16), Decl(divergentAccessorsTypes3.ts, 9, 36), Decl(divergentAccessorsTypes3.ts, 21, 36), Decl(divergentAccessorsTypes3.ts, 23, 36))
116+

0 commit comments

Comments
 (0)