Skip to content

Commit 461e29b

Browse files
authored
Merge pull request microsoft#18965 from Microsoft/set-symbol-on-union-of-spreads
Set symbol on union that is returned from `getSpreadType`
2 parents 73d06f7 + de68f06 commit 461e29b

File tree

6 files changed

+112
-104
lines changed

6 files changed

+112
-104
lines changed

src/compiler/checker.ts

+16-17
Original file line numberDiff line numberDiff line change
@@ -7878,7 +7878,7 @@ namespace ts {
78787878
* this function should be called in a left folding style, with left = previous result of getSpreadType
78797879
* and right = the new element to be spread.
78807880
*/
7881-
function getSpreadType(left: Type, right: Type): Type {
7881+
function getSpreadType(left: Type, right: Type, symbol: Symbol, propagatedFlags: TypeFlags): Type {
78827882
if (left.flags & TypeFlags.Any || right.flags & TypeFlags.Any) {
78837883
return anyType;
78847884
}
@@ -7889,10 +7889,10 @@ namespace ts {
78897889
return left;
78907890
}
78917891
if (left.flags & TypeFlags.Union) {
7892-
return mapType(left, t => getSpreadType(t, right));
7892+
return mapType(left, t => getSpreadType(t, right, symbol, propagatedFlags));
78937893
}
78947894
if (right.flags & TypeFlags.Union) {
7895-
return mapType(right, t => getSpreadType(left, t));
7895+
return mapType(right, t => getSpreadType(left, t, symbol, propagatedFlags));
78967896
}
78977897
if (right.flags & TypeFlags.NonPrimitive) {
78987898
return nonPrimitiveType;
@@ -7950,7 +7950,13 @@ namespace ts {
79507950
members.set(leftProp.escapedName, getNonReadonlySymbol(leftProp));
79517951
}
79527952
}
7953-
return createAnonymousType(undefined, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo);
7953+
7954+
const spread = createAnonymousType(undefined, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo);
7955+
spread.flags |= propagatedFlags;
7956+
spread.flags |= TypeFlags.FreshLiteral;
7957+
(spread as ObjectType).objectFlags |= ObjectFlags.ObjectLiteral;
7958+
spread.symbol = symbol;
7959+
return spread;
79547960
}
79557961

79567962
function getNonReadonlySymbol(prop: Symbol) {
@@ -13914,7 +13920,7 @@ namespace ts {
1391413920
checkExternalEmitHelpers(memberDecl, ExternalEmitHelpers.Assign);
1391513921
}
1391613922
if (propertiesArray.length > 0) {
13917-
spread = getSpreadType(spread, createObjectLiteralType());
13923+
spread = getSpreadType(spread, createObjectLiteralType(), node.symbol, propagatedFlags);
1391813924
propertiesArray = [];
1391913925
propertiesTable = createSymbolTable();
1392013926
hasComputedStringProperty = false;
@@ -13926,7 +13932,7 @@ namespace ts {
1392613932
error(memberDecl, Diagnostics.Spread_types_may_only_be_created_from_object_types);
1392713933
return unknownType;
1392813934
}
13929-
spread = getSpreadType(spread, type);
13935+
spread = getSpreadType(spread, type, node.symbol, propagatedFlags);
1393013936
offset = i + 1;
1393113937
continue;
1393213938
}
@@ -13971,14 +13977,7 @@ namespace ts {
1397113977

1397213978
if (spread !== emptyObjectType) {
1397313979
if (propertiesArray.length > 0) {
13974-
spread = getSpreadType(spread, createObjectLiteralType());
13975-
}
13976-
if (spread.flags & TypeFlags.Object) {
13977-
// only set the symbol and flags if this is a (fresh) object type
13978-
spread.flags |= propagatedFlags;
13979-
spread.flags |= TypeFlags.FreshLiteral;
13980-
(spread as ObjectType).objectFlags |= ObjectFlags.ObjectLiteral;
13981-
spread.symbol = node.symbol;
13980+
spread = getSpreadType(spread, createObjectLiteralType(), node.symbol, propagatedFlags);
1398213981
}
1398313982
return spread;
1398413983
}
@@ -14099,7 +14098,7 @@ namespace ts {
1409914098
else {
1410014099
Debug.assert(attributeDecl.kind === SyntaxKind.JsxSpreadAttribute);
1410114100
if (attributesArray.length > 0) {
14102-
spread = getSpreadType(spread, createJsxAttributesType(attributes.symbol, attributesTable));
14101+
spread = getSpreadType(spread, createJsxAttributesType(attributes.symbol, attributesTable), openingLikeElement.symbol, /*propagatedFlags*/ 0);
1410314102
attributesArray = [];
1410414103
attributesTable = createSymbolTable();
1410514104
}
@@ -14108,7 +14107,7 @@ namespace ts {
1410814107
hasSpreadAnyType = true;
1410914108
}
1411014109
if (isValidSpreadType(exprType)) {
14111-
spread = getSpreadType(spread, exprType);
14110+
spread = getSpreadType(spread, exprType, openingLikeElement.symbol, /*propagatedFlags*/ 0);
1411214111
}
1411314112
else {
1411414113
typeToIntersect = typeToIntersect ? getIntersectionType([typeToIntersect, exprType]) : exprType;
@@ -14119,7 +14118,7 @@ namespace ts {
1411914118
if (!hasSpreadAnyType) {
1412014119
if (spread !== emptyObjectType) {
1412114120
if (attributesArray.length > 0) {
14122-
spread = getSpreadType(spread, createJsxAttributesType(attributes.symbol, attributesTable));
14121+
spread = getSpreadType(spread, createJsxAttributesType(attributes.symbol, attributesTable), openingLikeElement.symbol, /*propagatedFlags*/ 0);
1412314122
}
1412414123
attributesArray = getPropertiesOfType(spread);
1412514124
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
tests/cases/conformance/types/spread/objectSpreadIndexSignature.ts(6,1): error TS7017: Element implicitly has an 'any' type because type '{ b: number; a: number; }' has no index signature.
2+
3+
4+
==== tests/cases/conformance/types/spread/objectSpreadIndexSignature.ts (1 errors) ====
5+
declare let indexed1: { [n: string]: number; a: number; };
6+
declare let indexed2: { [n: string]: boolean; c: boolean; };
7+
declare let indexed3: { [n: string]: number };
8+
let i = { ...indexed1, b: 11 };
9+
// only indexed has indexer, so i[101]: any
10+
i[101];
11+
~~~~~~
12+
!!! error TS7017: Element implicitly has an 'any' type because type '{ b: number; a: number; }' has no index signature.
13+
let ii = { ...indexed1, ...indexed2 };
14+
// both have indexer, so i[1001]: number | boolean
15+
ii[1001];
16+
17+
declare const b: boolean;
18+
indexed3 = { ...b ? indexed3 : undefined };
19+
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,20 @@
11
//// [objectSpreadIndexSignature.ts]
2-
interface Indexed {
3-
[n: string]: number;
4-
a: number;
5-
}
6-
interface Indexed2 {
7-
[n: string]: boolean;
8-
c: boolean;
9-
}
10-
let indexed: Indexed;
11-
let indexed2: Indexed2;
12-
let i = { ...indexed, b: 11 };
2+
declare let indexed1: { [n: string]: number; a: number; };
3+
declare let indexed2: { [n: string]: boolean; c: boolean; };
4+
declare let indexed3: { [n: string]: number };
5+
let i = { ...indexed1, b: 11 };
136
// only indexed has indexer, so i[101]: any
147
i[101];
15-
let ii = { ...indexed, ...indexed2 };
8+
let ii = { ...indexed1, ...indexed2 };
169
// both have indexer, so i[1001]: number | boolean
1710
ii[1001];
11+
12+
declare const b: boolean;
13+
indexed3 = { ...b ? indexed3 : undefined };
1814

1915

2016
//// [objectSpreadIndexSignature.js]
17+
"use strict";
2118
var __assign = (this && this.__assign) || Object.assign || function(t) {
2219
for (var s, i = 1, n = arguments.length; i < n; i++) {
2320
s = arguments[i];
@@ -26,11 +23,10 @@ var __assign = (this && this.__assign) || Object.assign || function(t) {
2623
}
2724
return t;
2825
};
29-
var indexed;
30-
var indexed2;
31-
var i = __assign({}, indexed, { b: 11 });
26+
var i = __assign({}, indexed1, { b: 11 });
3227
// only indexed has indexer, so i[101]: any
3328
i[101];
34-
var ii = __assign({}, indexed, indexed2);
29+
var ii = __assign({}, indexed1, indexed2);
3530
// both have indexer, so i[1001]: number | boolean
3631
ii[1001];
32+
indexed3 = __assign({}, b ? indexed3 : undefined);
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,42 @@
11
=== tests/cases/conformance/types/spread/objectSpreadIndexSignature.ts ===
2-
interface Indexed {
3-
>Indexed : Symbol(Indexed, Decl(objectSpreadIndexSignature.ts, 0, 0))
2+
declare let indexed1: { [n: string]: number; a: number; };
3+
>indexed1 : Symbol(indexed1, Decl(objectSpreadIndexSignature.ts, 0, 11))
4+
>n : Symbol(n, Decl(objectSpreadIndexSignature.ts, 0, 25))
5+
>a : Symbol(a, Decl(objectSpreadIndexSignature.ts, 0, 44))
46

5-
[n: string]: number;
6-
>n : Symbol(n, Decl(objectSpreadIndexSignature.ts, 1, 5))
7+
declare let indexed2: { [n: string]: boolean; c: boolean; };
8+
>indexed2 : Symbol(indexed2, Decl(objectSpreadIndexSignature.ts, 1, 11))
9+
>n : Symbol(n, Decl(objectSpreadIndexSignature.ts, 1, 25))
10+
>c : Symbol(c, Decl(objectSpreadIndexSignature.ts, 1, 45))
711

8-
a: number;
9-
>a : Symbol(Indexed.a, Decl(objectSpreadIndexSignature.ts, 1, 24))
10-
}
11-
interface Indexed2 {
12-
>Indexed2 : Symbol(Indexed2, Decl(objectSpreadIndexSignature.ts, 3, 1))
12+
declare let indexed3: { [n: string]: number };
13+
>indexed3 : Symbol(indexed3, Decl(objectSpreadIndexSignature.ts, 2, 11))
14+
>n : Symbol(n, Decl(objectSpreadIndexSignature.ts, 2, 25))
1315

14-
[n: string]: boolean;
15-
>n : Symbol(n, Decl(objectSpreadIndexSignature.ts, 5, 5))
16-
17-
c: boolean;
18-
>c : Symbol(Indexed2.c, Decl(objectSpreadIndexSignature.ts, 5, 25))
19-
}
20-
let indexed: Indexed;
21-
>indexed : Symbol(indexed, Decl(objectSpreadIndexSignature.ts, 8, 3))
22-
>Indexed : Symbol(Indexed, Decl(objectSpreadIndexSignature.ts, 0, 0))
23-
24-
let indexed2: Indexed2;
25-
>indexed2 : Symbol(indexed2, Decl(objectSpreadIndexSignature.ts, 9, 3))
26-
>Indexed2 : Symbol(Indexed2, Decl(objectSpreadIndexSignature.ts, 3, 1))
27-
28-
let i = { ...indexed, b: 11 };
29-
>i : Symbol(i, Decl(objectSpreadIndexSignature.ts, 10, 3))
30-
>indexed : Symbol(indexed, Decl(objectSpreadIndexSignature.ts, 8, 3))
31-
>b : Symbol(b, Decl(objectSpreadIndexSignature.ts, 10, 21))
16+
let i = { ...indexed1, b: 11 };
17+
>i : Symbol(i, Decl(objectSpreadIndexSignature.ts, 3, 3))
18+
>indexed1 : Symbol(indexed1, Decl(objectSpreadIndexSignature.ts, 0, 11))
19+
>b : Symbol(b, Decl(objectSpreadIndexSignature.ts, 3, 22))
3220

3321
// only indexed has indexer, so i[101]: any
3422
i[101];
35-
>i : Symbol(i, Decl(objectSpreadIndexSignature.ts, 10, 3))
23+
>i : Symbol(i, Decl(objectSpreadIndexSignature.ts, 3, 3))
3624

37-
let ii = { ...indexed, ...indexed2 };
38-
>ii : Symbol(ii, Decl(objectSpreadIndexSignature.ts, 13, 3))
39-
>indexed : Symbol(indexed, Decl(objectSpreadIndexSignature.ts, 8, 3))
40-
>indexed2 : Symbol(indexed2, Decl(objectSpreadIndexSignature.ts, 9, 3))
25+
let ii = { ...indexed1, ...indexed2 };
26+
>ii : Symbol(ii, Decl(objectSpreadIndexSignature.ts, 6, 3))
27+
>indexed1 : Symbol(indexed1, Decl(objectSpreadIndexSignature.ts, 0, 11))
28+
>indexed2 : Symbol(indexed2, Decl(objectSpreadIndexSignature.ts, 1, 11))
4129

4230
// both have indexer, so i[1001]: number | boolean
4331
ii[1001];
44-
>ii : Symbol(ii, Decl(objectSpreadIndexSignature.ts, 13, 3))
32+
>ii : Symbol(ii, Decl(objectSpreadIndexSignature.ts, 6, 3))
33+
34+
declare const b: boolean;
35+
>b : Symbol(b, Decl(objectSpreadIndexSignature.ts, 10, 13))
36+
37+
indexed3 = { ...b ? indexed3 : undefined };
38+
>indexed3 : Symbol(indexed3, Decl(objectSpreadIndexSignature.ts, 2, 11))
39+
>b : Symbol(b, Decl(objectSpreadIndexSignature.ts, 10, 13))
40+
>indexed3 : Symbol(indexed3, Decl(objectSpreadIndexSignature.ts, 2, 11))
41+
>undefined : Symbol(undefined)
4542

Original file line numberDiff line numberDiff line change
@@ -1,34 +1,22 @@
11
=== tests/cases/conformance/types/spread/objectSpreadIndexSignature.ts ===
2-
interface Indexed {
3-
>Indexed : Indexed
4-
5-
[n: string]: number;
2+
declare let indexed1: { [n: string]: number; a: number; };
3+
>indexed1 : { [n: string]: number; a: number; }
64
>n : string
7-
8-
a: number;
95
>a : number
10-
}
11-
interface Indexed2 {
12-
>Indexed2 : Indexed2
136

14-
[n: string]: boolean;
7+
declare let indexed2: { [n: string]: boolean; c: boolean; };
8+
>indexed2 : { [n: string]: boolean; c: boolean; }
159
>n : string
16-
17-
c: boolean;
1810
>c : boolean
19-
}
20-
let indexed: Indexed;
21-
>indexed : Indexed
22-
>Indexed : Indexed
2311

24-
let indexed2: Indexed2;
25-
>indexed2 : Indexed2
26-
>Indexed2 : Indexed2
12+
declare let indexed3: { [n: string]: number };
13+
>indexed3 : { [n: string]: number; }
14+
>n : string
2715

28-
let i = { ...indexed, b: 11 };
16+
let i = { ...indexed1, b: 11 };
2917
>i : { b: number; a: number; }
30-
>{ ...indexed, b: 11 } : { b: number; a: number; }
31-
>indexed : Indexed
18+
>{ ...indexed1, b: 11 } : { b: number; a: number; }
19+
>indexed1 : { [n: string]: number; a: number; }
3220
>b : number
3321
>11 : 11
3422

@@ -38,15 +26,27 @@ i[101];
3826
>i : { b: number; a: number; }
3927
>101 : 101
4028

41-
let ii = { ...indexed, ...indexed2 };
29+
let ii = { ...indexed1, ...indexed2 };
4230
>ii : { [x: string]: number | boolean; c: boolean; a: number; }
43-
>{ ...indexed, ...indexed2 } : { [x: string]: number | boolean; c: boolean; a: number; }
44-
>indexed : Indexed
45-
>indexed2 : Indexed2
31+
>{ ...indexed1, ...indexed2 } : { [x: string]: number | boolean; c: boolean; a: number; }
32+
>indexed1 : { [n: string]: number; a: number; }
33+
>indexed2 : { [n: string]: boolean; c: boolean; }
4634

4735
// both have indexer, so i[1001]: number | boolean
4836
ii[1001];
4937
>ii[1001] : number | boolean
5038
>ii : { [x: string]: number | boolean; c: boolean; a: number; }
5139
>1001 : 1001
5240

41+
declare const b: boolean;
42+
>b : boolean
43+
44+
indexed3 = { ...b ? indexed3 : undefined };
45+
>indexed3 = { ...b ? indexed3 : undefined } : {} | { [n: string]: number; }
46+
>indexed3 : { [n: string]: number; }
47+
>{ ...b ? indexed3 : undefined } : {} | { [n: string]: number; }
48+
>b ? indexed3 : undefined : { [n: string]: number; } | undefined
49+
>b : boolean
50+
>indexed3 : { [n: string]: number; }
51+
>undefined : undefined
52+
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
1-
interface Indexed {
2-
[n: string]: number;
3-
a: number;
4-
}
5-
interface Indexed2 {
6-
[n: string]: boolean;
7-
c: boolean;
8-
}
9-
let indexed: Indexed;
10-
let indexed2: Indexed2;
11-
let i = { ...indexed, b: 11 };
1+
// @strict: true
2+
declare let indexed1: { [n: string]: number; a: number; };
3+
declare let indexed2: { [n: string]: boolean; c: boolean; };
4+
declare let indexed3: { [n: string]: number };
5+
let i = { ...indexed1, b: 11 };
126
// only indexed has indexer, so i[101]: any
137
i[101];
14-
let ii = { ...indexed, ...indexed2 };
8+
let ii = { ...indexed1, ...indexed2 };
159
// both have indexer, so i[1001]: number | boolean
1610
ii[1001];
11+
12+
declare const b: boolean;
13+
indexed3 = { ...b ? indexed3 : undefined };

0 commit comments

Comments
 (0)