Skip to content

Commit a451953

Browse files
committed
Add __spreadArrays helper
1 parent 7423c69 commit a451953

33 files changed

+438
-240
lines changed

src/compiler/checker.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18671,8 +18671,8 @@ namespace ts {
1867118671
}
1867218672

1867318673
function checkSpreadExpression(node: SpreadElement, checkMode?: CheckMode): Type {
18674-
if (languageVersion < ScriptTarget.ES2015 && compilerOptions.downlevelIteration) {
18675-
checkExternalEmitHelpers(node, ExternalEmitHelpers.SpreadIncludes);
18674+
if (languageVersion < ScriptTarget.ES2015) {
18675+
checkExternalEmitHelpers(node, compilerOptions.downlevelIteration ? ExternalEmitHelpers.SpreadIncludes : ExternalEmitHelpers.SpreadArrays);
1867618676
}
1867718677

1867818678
const arrayOrIterableType = checkExpression(node.expression, checkMode);
@@ -30611,6 +30611,7 @@ namespace ts {
3061130611
case ExternalEmitHelpers.Values: return "__values";
3061230612
case ExternalEmitHelpers.Read: return "__read";
3061330613
case ExternalEmitHelpers.Spread: return "__spread";
30614+
case ExternalEmitHelpers.SpreadArrays: return "__spreadArrays";
3061430615
case ExternalEmitHelpers.Await: return "__await";
3061530616
case ExternalEmitHelpers.AsyncGenerator: return "__asyncGenerator";
3061630617
case ExternalEmitHelpers.AsyncDelegator: return "__asyncDelegator";

src/compiler/factory.ts

Lines changed: 25 additions & 0 deletions
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,30 @@ 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 ar = [], i = 0; i < arguments.length; i++)
3703+
for (var j = 0; j < arguments[i].length; j++)
3704+
ar.push(arguments[i][j]);
3705+
return ar;
3706+
};`
3707+
};
3708+
3709+
export function createSpreadArraysHelper(context: TransformationContext, argumentList: ReadonlyArray<Expression>, location?: TextRange) {
3710+
context.requestEmitHelper(spreadArraysHelper);
3711+
return setTextRange(
3712+
createCall(
3713+
getHelperName("__spreadArrays"),
3714+
/*typeArguments*/ undefined,
3715+
argumentList
3716+
),
3717+
location
3718+
);
3719+
}
3720+
36963721
// Utilities
36973722

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

src/compiler/transformers/es2015.ts

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3813,8 +3813,11 @@ namespace ts {
38133813
// [source]
38143814
// [a, ...b, c]
38153815
//
3816+
// [output (downlevelIteration)]
3817+
// __spread([a], b, [c])
3818+
//
38163819
// [output]
3817-
// [a].concat(b, [c])
3820+
// __spreadArrays([a], b, [c])
38183821

38193822
// Map spans of spread expressions into their expressions and spans of other
38203823
// expressions into an array literal.
@@ -3828,10 +3831,7 @@ namespace ts {
38283831
if (compilerOptions.downlevelIteration) {
38293832
if (segments.length === 1) {
38303833
const firstSegment = segments[0];
3831-
if (isCallExpression(firstSegment)
3832-
&& isIdentifier(firstSegment.expression)
3833-
&& (getEmitFlags(firstSegment.expression) & EmitFlags.HelperName)
3834-
&& firstSegment.expression.escapedText === "___spread") {
3834+
if (isCallToHelper(firstSegment, "___spread" as __String)) {
38353835
return segments[0];
38363836
}
38373837
}
@@ -3840,17 +3840,33 @@ namespace ts {
38403840
}
38413841
else {
38423842
if (segments.length === 1) {
3843-
const firstElement = elements[0];
3844-
return needsUniqueCopy && isSpreadElement(firstElement) && firstElement.expression.kind !== SyntaxKind.ArrayLiteralExpression
3845-
? createArraySlice(segments[0])
3846-
: segments[0];
3843+
const firstSegment = segments[0];
3844+
if (!needsUniqueCopy
3845+
|| isPackedArrayLiteral(firstSegment)
3846+
|| isCallToHelper(firstSegment, "___spreadArrays" as __String)) {
3847+
return segments[0];
3848+
}
38473849
}
38483850

3849-
// Rewrite using the pattern <segment0>.concat(<segment1>, <segment2>, ...)
3850-
return createArrayConcat(segments.shift()!, segments);
3851+
return createSpreadArraysHelper(context, segments);
38513852
}
38523853
}
38533854

3855+
function isPackedElement(node: Expression) {
3856+
return !isOmittedExpression(node);
3857+
}
3858+
3859+
function isPackedArrayLiteral(node: Expression) {
3860+
return isArrayLiteralExpression(node) && every(node.elements, isPackedElement);
3861+
}
3862+
3863+
function isCallToHelper(firstSegment: Expression, helperName: __String) {
3864+
return isCallExpression(firstSegment)
3865+
&& isIdentifier(firstSegment.expression)
3866+
&& (getEmitFlags(firstSegment.expression) & EmitFlags.HelperName)
3867+
&& firstSegment.expression.escapedText === helperName;
3868+
}
3869+
38543870
function partitionSpread(node: Expression) {
38553871
return isSpreadElement(node)
38563872
? visitSpanOfSpreads

src/compiler/types.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5307,12 +5307,13 @@ namespace ts {
53075307
Values = 1 << 8, // __values (used by ES2015 for..of and yield* transformations)
53085308
Read = 1 << 9, // __read (used by ES2015 iterator destructuring transformation)
53095309
Spread = 1 << 10, // __spread (used by ES2015 array spread and argument list spread transformations)
5310-
Await = 1 << 11, // __await (used by ES2017 async generator transformation)
5311-
AsyncGenerator = 1 << 12, // __asyncGenerator (used by ES2017 async generator transformation)
5312-
AsyncDelegator = 1 << 13, // __asyncDelegator (used by ES2017 async generator yield* transformation)
5313-
AsyncValues = 1 << 14, // __asyncValues (used by ES2017 for..await..of transformation)
5314-
ExportStar = 1 << 15, // __exportStar (used by CommonJS/AMD/UMD module transformation)
5315-
MakeTemplateObject = 1 << 16, // __makeTemplateObject (used for constructing template string array objects)
5310+
SpreadArrays = 1 << 11, // __spreadArrays (used by ES2015 array spread and argument list spread transformations)
5311+
Await = 1 << 12, // __await (used by ES2017 async generator transformation)
5312+
AsyncGenerator = 1 << 13, // __asyncGenerator (used by ES2017 async generator transformation)
5313+
AsyncDelegator = 1 << 14, // __asyncDelegator (used by ES2017 async generator yield* transformation)
5314+
AsyncValues = 1 << 15, // __asyncValues (used by ES2017 for..await..of transformation)
5315+
ExportStar = 1 << 16, // __exportStar (used by CommonJS/AMD/UMD module transformation)
5316+
MakeTemplateObject = 1 << 17, // __makeTemplateObject (used for constructing template string array objects)
53165317
FirstEmitHelper = Extends,
53175318
LastEmitHelper = MakeTemplateObject,
53185319

tests/baselines/reference/argumentExpressionContextualTyping.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ 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 ar = [], i = 0; i < arguments.length; i++)
24+
for (var j = 0; j < arguments[i].length; j++)
25+
ar.push(arguments[i][j]);
26+
return ar;
27+
};
2228
// In a typed function call, argument expressions are contextually typed by their corresponding parameter types.
2329
function foo(_a) {
2430
var _b = _a.x, a = _b[0], b = _b[1], _c = _a.y, c = _c.c, d = _c.d, e = _c.e;
@@ -36,5 +42,5 @@ var tuple = ["string", 1, true];
3642
baz(tuple);
3743
baz(["string", 1, true]);
3844
baz(array); // Error
39-
baz(["string", 1, true].concat(array)); // Error
45+
baz(__spreadArrays(["string", 1, true], array)); // Error
4046
foo(o); // Error because x has an array type namely (string|number)[]

tests/baselines/reference/arrayLiteralExpressionContextualTyping.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ 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 ar = [], i = 0; i < arguments.length; i++)
21+
for (var j = 0; j < arguments[i].length; j++)
22+
ar.push(arguments[i][j]);
23+
return ar;
24+
};
1925
// In a contextually typed array literal expression containing no spread elements, an element expression at index N is contextually typed by
2026
// the type of the property with the numeric name N in the contextual type, if any, or otherwise
2127
// the numeric index type of the contextual type, if any.
@@ -26,6 +32,6 @@ var tup1 = [1, 2, 3, "string"];
2632
var tup2 = [1, 2, 3, "string"]; // Error
2733
// In a contextually typed array literal expression containing one or more spread elements,
2834
// 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
35+
var spr = __spreadArrays([1, 2, 3], array);
36+
var spr1 = __spreadArrays([1, 2, 3], tup);
37+
var spr2 = __spreadArrays([1, 2, 3], tup); // Error

tests/baselines/reference/arrayLiteralSpread.js

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,20 +24,26 @@ function f2() {
2424

2525

2626
//// [arrayLiteralSpread.js]
27+
var __spreadArrays = (this && this.__spreadArrays) || function () {
28+
for (var ar = [], i = 0; i < arguments.length; i++)
29+
for (var j = 0; j < arguments[i].length; j++)
30+
ar.push(arguments[i][j]);
31+
return ar;
32+
};
2733
function f0() {
2834
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);
35+
var a1 = __spreadArrays(a);
36+
var a2 = __spreadArrays([1], a);
37+
var a3 = __spreadArrays([1, 2], a);
38+
var a4 = __spreadArrays(a, [1]);
39+
var a5 = __spreadArrays(a, [1, 2]);
40+
var a6 = __spreadArrays([1, 2], a, [1, 2]);
41+
var a7 = __spreadArrays([1], a, [2], a);
42+
var a8 = __spreadArrays(a, a, a);
3743
}
3844
function f1() {
3945
var a = [1, 2, 3];
40-
var b = ["hello"].concat(a, [true]);
46+
var b = __spreadArrays(["hello"], a, [true]);
4147
var b;
4248
}
4349
function f2() {

tests/baselines/reference/arrayLiterals2ES5.js

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,20 @@ 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 ar = [], i = 0; i < arguments.length; i++)
67+
for (var j = 0; j < arguments[i].length; j++)
68+
ar.push(arguments[i][j]);
69+
return ar;
70+
};
6571
// SpreadElement:
6672
// ... AssignmentExpression
6773
var a0 = [, , 2, 3, 4];
6874
var a1 = ["hello", "world"];
69-
var a2 = [, , ].concat(a0, ["hello"]);
70-
var a3 = [, ].concat(a0);
75+
var a2 = __spreadArrays([, , ], a0, ["hello"]);
76+
var a3 = __spreadArrays([, ], a0);
7177
var a4 = [function () { return 1; },];
72-
var a5 = a0.concat([,]);
78+
var a5 = __spreadArrays(a0, [,]);
7379
// Each element expression in a non-empty array literal is processed as follows:
7480
// - If the array literal contains no spread elements, and if the array literal is contextually typed (section 4.19)
7581
// 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 +98,13 @@ var temp1 = [1, 2, 3];
9298
var temp2 = [[1, 2, 3], ["hello", "string"]];
9399
var temp3 = [undefined, null, undefined];
94100
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"]);
101+
var d0 = __spreadArrays([1, true], temp); // has type (string|number|boolean)[]
102+
var d1 = __spreadArrays(temp); // has type string[]
103+
var d2 = __spreadArrays(temp1);
104+
var d3 = __spreadArrays(temp1);
105+
var d4 = __spreadArrays(temp, temp1);
106+
var d5 = __spreadArrays(temp3);
107+
var d6 = __spreadArrays(temp4);
108+
var d7 = __spreadArrays(temp1);
109+
var d8 = [__spreadArrays(temp1)];
110+
var d9 = __spreadArrays([__spreadArrays(temp1)], ["hello"]);

tests/baselines/reference/arrayLiterals3.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@ 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 ar = [], i = 0; i < arguments.length; i++)
45+
for (var j = 0; j < arguments[i].length; j++)
46+
ar.push(arguments[i][j]);
47+
return ar;
48+
};
4349
// The resulting type an array literal expression is determined as follows:
4450
// - If the array literal contains no spread elements and is contextually typed by a tuple-like type,
4551
// the resulting type is a tuple type constructed from the types of the element expressions.
@@ -55,6 +61,6 @@ var _a = [1, 2, "string", true], b1 = _a[0], b2 = _a[1];
5561
var temp = ["s", "t", "r"];
5662
var temp1 = [1, 2, 3];
5763
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[]
64+
var c0 = __spreadArrays(temp2); // Error
65+
var c1 = __spreadArrays(temp1); // Error cannot assign number[] to [number, number, number]
66+
var c2 = __spreadArrays(temp1, temp); // Error cannot assign (number|string)[] to number[]

tests/baselines/reference/callOverload.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,16 @@ withRest();
1212
withRest(...n);
1313

1414
//// [callOverload.js]
15+
var __spreadArrays = (this && this.__spreadArrays) || function () {
16+
for (var ar = [], i = 0; i < arguments.length; i++)
17+
for (var j = 0; j < arguments[i].length; j++)
18+
ar.push(arguments[i][j]);
19+
return ar;
20+
};
1521
var n;
1622
fn(1); // no error
1723
fn(1, 2, 3, 4);
1824
takeTwo(1, 2, 3, 4);
19-
withRest.apply(void 0, ['a'].concat(n)); // no error
25+
withRest.apply(void 0, __spreadArrays(['a'], n)); // no error
2026
withRest();
2127
withRest.apply(void 0, n);

tests/baselines/reference/callWithSpread.js

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,12 @@ 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 ar = [], i = 0; i < arguments.length; i++)
77+
for (var j = 0; j < arguments[i].length; j++)
78+
ar.push(arguments[i][j]);
79+
return ar;
80+
};
7581
var _a, _b, _c, _d, _e, _f, _g;
7682
function foo(x, y) {
7783
var z = [];
@@ -84,23 +90,23 @@ var z;
8490
var obj;
8591
var xa;
8692
foo(1, 2, "abc");
87-
foo.apply(void 0, [1, 2].concat(a));
88-
foo.apply(void 0, [1, 2].concat(a, ["abc"]));
93+
foo.apply(void 0, __spreadArrays([1, 2], a));
94+
foo.apply(void 0, __spreadArrays([1, 2], a, ["abc"]));
8995
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"]));
96+
obj.foo.apply(obj, __spreadArrays([1, 2], a));
97+
obj.foo.apply(obj, __spreadArrays([1, 2], a, ["abc"]));
98+
obj.foo.apply(obj, __spreadArrays([1, 2], a)).foo(1, 2, "abc");
99+
(_a = obj.foo.apply(obj, __spreadArrays([1, 2], a))).foo.apply(_a, __spreadArrays([1, 2], a));
100+
(_b = obj.foo.apply(obj, __spreadArrays([1, 2], a))).foo.apply(_b, __spreadArrays([1, 2], a, ["abc"]));
95101
(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"]));
102+
obj.foo.apply(obj, __spreadArrays([1, 2], a));
103+
obj.foo.apply(obj, __spreadArrays([1, 2], a, ["abc"]));
104+
(obj.foo.apply(obj, __spreadArrays([1, 2], a)).foo)(1, 2, "abc");
105+
(_c = obj.foo.apply(obj, __spreadArrays([1, 2], a))).foo.apply(_c, __spreadArrays([1, 2], a));
106+
(_d = obj.foo.apply(obj, __spreadArrays([1, 2], a))).foo.apply(_d, __spreadArrays([1, 2], a, ["abc"]));
101107
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"]));
108+
(_e = xa[1]).foo.apply(_e, __spreadArrays([1, 2], a));
109+
(_f = xa[1]).foo.apply(_f, __spreadArrays([1, 2], a, ["abc"]));
104110
(_g = xa[1]).foo.apply(_g, [1, 2, "abc"]);
105111
var C = /** @class */ (function () {
106112
function C(x, y) {
@@ -109,7 +115,7 @@ var C = /** @class */ (function () {
109115
z[_i - 2] = arguments[_i];
110116
}
111117
this.foo(x, y);
112-
this.foo.apply(this, [x, y].concat(z));
118+
this.foo.apply(this, __spreadArrays([x, y], z));
113119
}
114120
C.prototype.foo = function (x, y) {
115121
var z = [];
@@ -123,12 +129,12 @@ var D = /** @class */ (function (_super) {
123129
__extends(D, _super);
124130
function D() {
125131
var _this = _super.call(this, 1, 2) || this;
126-
_this = _super.apply(this, [1, 2].concat(a)) || this;
132+
_this = _super.apply(this, __spreadArrays([1, 2], a)) || this;
127133
return _this;
128134
}
129135
D.prototype.foo = function () {
130136
_super.prototype.foo.call(this, 1, 2);
131-
_super.prototype.foo.apply(this, [1, 2].concat(a));
137+
_super.prototype.foo.apply(this, __spreadArrays([1, 2], a));
132138
};
133139
return D;
134140
}(C));

0 commit comments

Comments
 (0)