Skip to content

Commit 375487e

Browse files
authored
Add __spreadArrays helper (microsoft#31166)
1 parent ad322a5 commit 375487e

33 files changed

+465
-240
lines changed

src/compiler/checker.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -19080,8 +19080,8 @@ namespace ts {
1908019080
}
1908119081

1908219082
function checkSpreadExpression(node: SpreadElement, checkMode?: CheckMode): Type {
19083-
if (languageVersion < ScriptTarget.ES2015 && compilerOptions.downlevelIteration) {
19084-
checkExternalEmitHelpers(node, ExternalEmitHelpers.SpreadIncludes);
19083+
if (languageVersion < ScriptTarget.ES2015) {
19084+
checkExternalEmitHelpers(node, compilerOptions.downlevelIteration ? ExternalEmitHelpers.SpreadIncludes : ExternalEmitHelpers.SpreadArrays);
1908519085
}
1908619086

1908719087
const arrayOrIterableType = checkExpression(node.expression, checkMode);
@@ -31104,6 +31104,7 @@ namespace ts {
3110431104
case ExternalEmitHelpers.Values: return "__values";
3110531105
case ExternalEmitHelpers.Read: return "__read";
3110631106
case ExternalEmitHelpers.Spread: return "__spread";
31107+
case ExternalEmitHelpers.SpreadArrays: return "__spreadArrays";
3110731108
case ExternalEmitHelpers.Await: return "__await";
3110831109
case ExternalEmitHelpers.AsyncGenerator: return "__asyncGenerator";
3110931110
case ExternalEmitHelpers.AsyncDelegator: return "__asyncDelegator";

src/compiler/factory.ts

+26
Original file line numberDiff line numberDiff line change
@@ -2657,6 +2657,7 @@ namespace ts {
26572657
valuesHelper,
26582658
readHelper,
26592659
spreadHelper,
2660+
spreadArraysHelper,
26602661
restHelper,
26612662
decorateHelper,
26622663
metadataHelper,
@@ -3693,6 +3694,31 @@ namespace ts {
36933694
);
36943695
}
36953696

3697+
export const spreadArraysHelper: UnscopedEmitHelper = {
3698+
name: "typescript:spreadArrays",
3699+
scoped: false,
3700+
text: `
3701+
var __spreadArrays = (this && this.__spreadArrays) || function () {
3702+
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
3703+
for (var r = Array(s), k = 0, i = 0; i < il; i++)
3704+
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
3705+
r[k] = a[j];
3706+
return r;
3707+
};`
3708+
};
3709+
3710+
export function createSpreadArraysHelper(context: TransformationContext, argumentList: ReadonlyArray<Expression>, location?: TextRange) {
3711+
context.requestEmitHelper(spreadArraysHelper);
3712+
return setTextRange(
3713+
createCall(
3714+
getHelperName("__spreadArrays"),
3715+
/*typeArguments*/ undefined,
3716+
argumentList
3717+
),
3718+
location
3719+
);
3720+
}
3721+
36963722
// Utilities
36973723

36983724
export function createForOfBindingStatement(node: ForInitializer, boundValue: Expression): Statement {

src/compiler/transformers/es2015.ts

+27-11
Original file line numberDiff line numberDiff line change
@@ -3819,8 +3819,11 @@ namespace ts {
38193819
// [source]
38203820
// [a, ...b, c]
38213821
//
3822+
// [output (downlevelIteration)]
3823+
// __spread([a], b, [c])
3824+
//
38223825
// [output]
3823-
// [a].concat(b, [c])
3826+
// __spreadArrays([a], b, [c])
38243827

38253828
// Map spans of spread expressions into their expressions and spans of other
38263829
// expressions into an array literal.
@@ -3834,10 +3837,7 @@ namespace ts {
38343837
if (compilerOptions.downlevelIteration) {
38353838
if (segments.length === 1) {
38363839
const firstSegment = segments[0];
3837-
if (isCallExpression(firstSegment)
3838-
&& isIdentifier(firstSegment.expression)
3839-
&& (getEmitFlags(firstSegment.expression) & EmitFlags.HelperName)
3840-
&& firstSegment.expression.escapedText === "___spread") {
3840+
if (isCallToHelper(firstSegment, "___spread" as __String)) {
38413841
return segments[0];
38423842
}
38433843
}
@@ -3846,17 +3846,33 @@ namespace ts {
38463846
}
38473847
else {
38483848
if (segments.length === 1) {
3849-
const firstElement = elements[0];
3850-
return needsUniqueCopy && isSpreadElement(firstElement) && firstElement.expression.kind !== SyntaxKind.ArrayLiteralExpression
3851-
? createArraySlice(segments[0])
3852-
: segments[0];
3849+
const firstSegment = segments[0];
3850+
if (!needsUniqueCopy
3851+
|| isPackedArrayLiteral(firstSegment)
3852+
|| isCallToHelper(firstSegment, "___spreadArrays" as __String)) {
3853+
return segments[0];
3854+
}
38533855
}
38543856

3855-
// Rewrite using the pattern <segment0>.concat(<segment1>, <segment2>, ...)
3856-
return createArrayConcat(segments.shift()!, segments);
3857+
return createSpreadArraysHelper(context, segments);
38573858
}
38583859
}
38593860

3861+
function isPackedElement(node: Expression) {
3862+
return !isOmittedExpression(node);
3863+
}
3864+
3865+
function isPackedArrayLiteral(node: Expression) {
3866+
return isArrayLiteralExpression(node) && every(node.elements, isPackedElement);
3867+
}
3868+
3869+
function isCallToHelper(firstSegment: Expression, helperName: __String) {
3870+
return isCallExpression(firstSegment)
3871+
&& isIdentifier(firstSegment.expression)
3872+
&& (getEmitFlags(firstSegment.expression) & EmitFlags.HelperName)
3873+
&& firstSegment.expression.escapedText === helperName;
3874+
}
3875+
38603876
function partitionSpread(node: Expression) {
38613877
return isSpreadElement(node)
38623878
? visitSpanOfSpreads

src/compiler/types.ts

+7-6
Original file line numberDiff line numberDiff line change
@@ -5339,12 +5339,13 @@ namespace ts {
53395339
Values = 1 << 8, // __values (used by ES2015 for..of and yield* transformations)
53405340
Read = 1 << 9, // __read (used by ES2015 iterator destructuring transformation)
53415341
Spread = 1 << 10, // __spread (used by ES2015 array spread and argument list spread transformations)
5342-
Await = 1 << 11, // __await (used by ES2017 async generator transformation)
5343-
AsyncGenerator = 1 << 12, // __asyncGenerator (used by ES2017 async generator transformation)
5344-
AsyncDelegator = 1 << 13, // __asyncDelegator (used by ES2017 async generator yield* transformation)
5345-
AsyncValues = 1 << 14, // __asyncValues (used by ES2017 for..await..of transformation)
5346-
ExportStar = 1 << 15, // __exportStar (used by CommonJS/AMD/UMD module transformation)
5347-
MakeTemplateObject = 1 << 16, // __makeTemplateObject (used for constructing template string array objects)
5342+
SpreadArrays = 1 << 11, // __spreadArrays (used by ES2015 array spread and argument list spread transformations)
5343+
Await = 1 << 12, // __await (used by ES2017 async generator transformation)
5344+
AsyncGenerator = 1 << 13, // __asyncGenerator (used by ES2017 async generator transformation)
5345+
AsyncDelegator = 1 << 14, // __asyncDelegator (used by ES2017 async generator yield* transformation)
5346+
AsyncValues = 1 << 15, // __asyncValues (used by ES2017 for..await..of transformation)
5347+
ExportStar = 1 << 16, // __exportStar (used by CommonJS/AMD/UMD module transformation)
5348+
MakeTemplateObject = 1 << 17, // __makeTemplateObject (used for constructing template string array objects)
53485349
FirstEmitHelper = Extends,
53495350
LastEmitHelper = MakeTemplateObject,
53505351

tests/baselines/reference/argumentExpressionContextualTyping.js

+8-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@ baz(["string", 1, true, ...array]); // Error
1919
foo(o); // Error because x has an array type namely (string|number)[]
2020

2121
//// [argumentExpressionContextualTyping.js]
22+
var __spreadArrays = (this && this.__spreadArrays) || function () {
23+
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
24+
for (var r = Array(s), k = 0, i = 0; i < il; i++)
25+
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
26+
r[k] = a[j];
27+
return r;
28+
};
2229
// In a typed function call, argument expressions are contextually typed by their corresponding parameter types.
2330
function foo(_a) {
2431
var _b = _a.x, a = _b[0], b = _b[1], _c = _a.y, c = _c.c, d = _c.d, e = _c.e;
@@ -36,5 +43,5 @@ var tuple = ["string", 1, true];
3643
baz(tuple);
3744
baz(["string", 1, true]);
3845
baz(array); // Error
39-
baz(["string", 1, true].concat(array)); // Error
46+
baz(__spreadArrays(["string", 1, true], array)); // Error
4047
foo(o); // Error because x has an array type namely (string|number)[]

tests/baselines/reference/arrayLiteralExpressionContextualTyping.js

+10-3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ var spr2:[number, number, number] = [1, 2, 3, ...tup]; // Error
1616

1717

1818
//// [arrayLiteralExpressionContextualTyping.js]
19+
var __spreadArrays = (this && this.__spreadArrays) || function () {
20+
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
21+
for (var r = Array(s), k = 0, i = 0; i < il; i++)
22+
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
23+
r[k] = a[j];
24+
return r;
25+
};
1926
// In a contextually typed array literal expression containing no spread elements, an element expression at index N is contextually typed by
2027
// the type of the property with the numeric name N in the contextual type, if any, or otherwise
2128
// the numeric index type of the contextual type, if any.
@@ -26,6 +33,6 @@ var tup1 = [1, 2, 3, "string"];
2633
var tup2 = [1, 2, 3, "string"]; // Error
2734
// In a contextually typed array literal expression containing one or more spread elements,
2835
// an element expression at index N is contextually typed by the numeric index type of the contextual type, if any.
29-
var spr = [1, 2, 3].concat(array);
30-
var spr1 = [1, 2, 3].concat(tup);
31-
var spr2 = [1, 2, 3].concat(tup); // Error
36+
var spr = __spreadArrays([1, 2, 3], array);
37+
var spr1 = __spreadArrays([1, 2, 3], tup);
38+
var spr2 = __spreadArrays([1, 2, 3], tup); // Error

tests/baselines/reference/arrayLiteralSpread.js

+16-9
Original file line numberDiff line numberDiff line change
@@ -24,20 +24,27 @@ function f2() {
2424

2525

2626
//// [arrayLiteralSpread.js]
27+
var __spreadArrays = (this && this.__spreadArrays) || function () {
28+
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
29+
for (var r = Array(s), k = 0, i = 0; i < il; i++)
30+
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
31+
r[k] = a[j];
32+
return r;
33+
};
2734
function f0() {
2835
var a = [1, 2, 3];
29-
var a1 = a.slice();
30-
var a2 = [1].concat(a);
31-
var a3 = [1, 2].concat(a);
32-
var a4 = a.concat([1]);
33-
var a5 = a.concat([1, 2]);
34-
var a6 = [1, 2].concat(a, [1, 2]);
35-
var a7 = [1].concat(a, [2], a);
36-
var a8 = a.concat(a, a);
36+
var a1 = __spreadArrays(a);
37+
var a2 = __spreadArrays([1], a);
38+
var a3 = __spreadArrays([1, 2], a);
39+
var a4 = __spreadArrays(a, [1]);
40+
var a5 = __spreadArrays(a, [1, 2]);
41+
var a6 = __spreadArrays([1, 2], a, [1, 2]);
42+
var a7 = __spreadArrays([1], a, [2], a);
43+
var a8 = __spreadArrays(a, a, a);
3744
}
3845
function f1() {
3946
var a = [1, 2, 3];
40-
var b = ["hello"].concat(a, [true]);
47+
var b = __spreadArrays(["hello"], a, [true]);
4148
var b;
4249
}
4350
function f2() {

tests/baselines/reference/arrayLiterals2ES5.js

+20-13
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,21 @@ var d9 = [[...temp1], ...["hello"]];
6262
// Elisionopt SpreadElement
6363
// ElementList, Elisionopt AssignmentExpression
6464
// ElementList, Elisionopt SpreadElement
65+
var __spreadArrays = (this && this.__spreadArrays) || function () {
66+
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
67+
for (var r = Array(s), k = 0, i = 0; i < il; i++)
68+
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
69+
r[k] = a[j];
70+
return r;
71+
};
6572
// SpreadElement:
6673
// ... AssignmentExpression
6774
var a0 = [, , 2, 3, 4];
6875
var a1 = ["hello", "world"];
69-
var a2 = [, , ].concat(a0, ["hello"]);
70-
var a3 = [, ].concat(a0);
76+
var a2 = __spreadArrays([, , ], a0, ["hello"]);
77+
var a3 = __spreadArrays([, ], a0);
7178
var a4 = [function () { return 1; },];
72-
var a5 = a0.concat([,]);
79+
var a5 = __spreadArrays(a0, [,]);
7380
// Each element expression in a non-empty array literal is processed as follows:
7481
// - If the array literal contains no spread elements, and if the array literal is contextually typed (section 4.19)
7582
// by a type T and T has a property with the numeric name N, where N is the index of the element expression in the array literal,
@@ -92,13 +99,13 @@ var temp1 = [1, 2, 3];
9299
var temp2 = [[1, 2, 3], ["hello", "string"]];
93100
var temp3 = [undefined, null, undefined];
94101
var temp4 = [];
95-
var d0 = [1, true].concat(temp); // has type (string|number|boolean)[]
96-
var d1 = temp.slice(); // has type string[]
97-
var d2 = temp1.slice();
98-
var d3 = temp1.slice();
99-
var d4 = temp.concat(temp1);
100-
var d5 = temp3.slice();
101-
var d6 = temp4.slice();
102-
var d7 = temp1.slice();
103-
var d8 = [temp1.slice()];
104-
var d9 = [temp1.slice()].concat(["hello"]);
102+
var d0 = __spreadArrays([1, true], temp); // has type (string|number|boolean)[]
103+
var d1 = __spreadArrays(temp); // has type string[]
104+
var d2 = __spreadArrays(temp1);
105+
var d3 = __spreadArrays(temp1);
106+
var d4 = __spreadArrays(temp, temp1);
107+
var d5 = __spreadArrays(temp3);
108+
var d6 = __spreadArrays(temp4);
109+
var d7 = __spreadArrays(temp1);
110+
var d8 = [__spreadArrays(temp1)];
111+
var d9 = __spreadArrays([__spreadArrays(temp1)], ["hello"]);

tests/baselines/reference/arrayLiterals3.js

+10-3
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,13 @@ var c2: myArray = [...temp1, ...temp]; // Error cannot assign (number
4040
// - If the array literal contains no spread elements, and if the array literal is contextually typed (section 4.19)
4141
// by a type T and T has a property with the numeric name N, where N is the index of the element expression in the array literal,
4242
// the element expression is contextually typed by the type of that property.
43+
var __spreadArrays = (this && this.__spreadArrays) || function () {
44+
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
45+
for (var r = Array(s), k = 0, i = 0; i < il; i++)
46+
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
47+
r[k] = a[j];
48+
return r;
49+
};
4350
// The resulting type an array literal expression is determined as follows:
4451
// - If the array literal contains no spread elements and is contextually typed by a tuple-like type,
4552
// the resulting type is a tuple type constructed from the types of the element expressions.
@@ -55,6 +62,6 @@ var _a = [1, 2, "string", true], b1 = _a[0], b2 = _a[1];
5562
var temp = ["s", "t", "r"];
5663
var temp1 = [1, 2, 3];
5764
var temp2 = [[1, 2, 3], ["hello", "string"]];
58-
var c0 = temp2.slice(); // Error
59-
var c1 = temp1.slice(); // Error cannot assign number[] to [number, number, number]
60-
var c2 = temp1.concat(temp); // Error cannot assign (number|string)[] to number[]
65+
var c0 = __spreadArrays(temp2); // Error
66+
var c1 = __spreadArrays(temp1); // Error cannot assign number[] to [number, number, number]
67+
var c2 = __spreadArrays(temp1, temp); // Error cannot assign (number|string)[] to number[]

tests/baselines/reference/callOverload.js

+8-1
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,17 @@ withRest();
1212
withRest(...n);
1313

1414
//// [callOverload.js]
15+
var __spreadArrays = (this && this.__spreadArrays) || function () {
16+
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
17+
for (var r = Array(s), k = 0, i = 0; i < il; i++)
18+
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
19+
r[k] = a[j];
20+
return r;
21+
};
1522
var n;
1623
fn(1); // no error
1724
fn(1, 2, 3, 4);
1825
takeTwo(1, 2, 3, 4);
19-
withRest.apply(void 0, ['a'].concat(n)); // no error
26+
withRest.apply(void 0, __spreadArrays(['a'], n)); // no error
2027
withRest();
2128
withRest.apply(void 0, n);

tests/baselines/reference/callWithSpread.js

+24-17
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,13 @@ var __extends = (this && this.__extends) || (function () {
7272
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
7373
};
7474
})();
75+
var __spreadArrays = (this && this.__spreadArrays) || function () {
76+
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
77+
for (var r = Array(s), k = 0, i = 0; i < il; i++)
78+
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
79+
r[k] = a[j];
80+
return r;
81+
};
7582
var _a, _b, _c, _d, _e, _f, _g;
7683
function foo(x, y) {
7784
var z = [];
@@ -84,23 +91,23 @@ var z;
8491
var obj;
8592
var xa;
8693
foo(1, 2, "abc");
87-
foo.apply(void 0, [1, 2].concat(a));
88-
foo.apply(void 0, [1, 2].concat(a, ["abc"]));
94+
foo.apply(void 0, __spreadArrays([1, 2], a));
95+
foo.apply(void 0, __spreadArrays([1, 2], a, ["abc"]));
8996
obj.foo(1, 2, "abc");
90-
obj.foo.apply(obj, [1, 2].concat(a));
91-
obj.foo.apply(obj, [1, 2].concat(a, ["abc"]));
92-
obj.foo.apply(obj, [1, 2].concat(a)).foo(1, 2, "abc");
93-
(_a = obj.foo.apply(obj, [1, 2].concat(a))).foo.apply(_a, [1, 2].concat(a));
94-
(_b = obj.foo.apply(obj, [1, 2].concat(a))).foo.apply(_b, [1, 2].concat(a, ["abc"]));
97+
obj.foo.apply(obj, __spreadArrays([1, 2], a));
98+
obj.foo.apply(obj, __spreadArrays([1, 2], a, ["abc"]));
99+
obj.foo.apply(obj, __spreadArrays([1, 2], a)).foo(1, 2, "abc");
100+
(_a = obj.foo.apply(obj, __spreadArrays([1, 2], a))).foo.apply(_a, __spreadArrays([1, 2], a));
101+
(_b = obj.foo.apply(obj, __spreadArrays([1, 2], a))).foo.apply(_b, __spreadArrays([1, 2], a, ["abc"]));
95102
(obj.foo)(1, 2, "abc");
96-
obj.foo.apply(obj, [1, 2].concat(a));
97-
obj.foo.apply(obj, [1, 2].concat(a, ["abc"]));
98-
(obj.foo.apply(obj, [1, 2].concat(a)).foo)(1, 2, "abc");
99-
(_c = obj.foo.apply(obj, [1, 2].concat(a))).foo.apply(_c, [1, 2].concat(a));
100-
(_d = obj.foo.apply(obj, [1, 2].concat(a))).foo.apply(_d, [1, 2].concat(a, ["abc"]));
103+
obj.foo.apply(obj, __spreadArrays([1, 2], a));
104+
obj.foo.apply(obj, __spreadArrays([1, 2], a, ["abc"]));
105+
(obj.foo.apply(obj, __spreadArrays([1, 2], a)).foo)(1, 2, "abc");
106+
(_c = obj.foo.apply(obj, __spreadArrays([1, 2], a))).foo.apply(_c, __spreadArrays([1, 2], a));
107+
(_d = obj.foo.apply(obj, __spreadArrays([1, 2], a))).foo.apply(_d, __spreadArrays([1, 2], a, ["abc"]));
101108
xa[1].foo(1, 2, "abc");
102-
(_e = xa[1]).foo.apply(_e, [1, 2].concat(a));
103-
(_f = xa[1]).foo.apply(_f, [1, 2].concat(a, ["abc"]));
109+
(_e = xa[1]).foo.apply(_e, __spreadArrays([1, 2], a));
110+
(_f = xa[1]).foo.apply(_f, __spreadArrays([1, 2], a, ["abc"]));
104111
(_g = xa[1]).foo.apply(_g, [1, 2, "abc"]);
105112
var C = /** @class */ (function () {
106113
function C(x, y) {
@@ -109,7 +116,7 @@ var C = /** @class */ (function () {
109116
z[_i - 2] = arguments[_i];
110117
}
111118
this.foo(x, y);
112-
this.foo.apply(this, [x, y].concat(z));
119+
this.foo.apply(this, __spreadArrays([x, y], z));
113120
}
114121
C.prototype.foo = function (x, y) {
115122
var z = [];
@@ -123,12 +130,12 @@ var D = /** @class */ (function (_super) {
123130
__extends(D, _super);
124131
function D() {
125132
var _this = _super.call(this, 1, 2) || this;
126-
_this = _super.apply(this, [1, 2].concat(a)) || this;
133+
_this = _super.apply(this, __spreadArrays([1, 2], a)) || this;
127134
return _this;
128135
}
129136
D.prototype.foo = function () {
130137
_super.prototype.foo.call(this, 1, 2);
131-
_super.prototype.foo.apply(this, [1, 2].concat(a));
138+
_super.prototype.foo.apply(this, __spreadArrays([1, 2], a));
132139
};
133140
return D;
134141
}(C));

0 commit comments

Comments
 (0)