From d13081685b0b471cbec7aa31e8b06b38f7f6ef61 Mon Sep 17 00:00:00 2001 From: Tingan Ho Date: Wed, 13 May 2015 09:36:40 +0800 Subject: [PATCH] Adds spread syntax in new expression in ES5 --- src/compiler/checker.ts | 8 ++-- .../diagnosticInformationMap.generated.ts | 2 +- src/compiler/diagnosticMessages.json | 2 +- src/compiler/emitter.ts | 39 +++++++++++++++++-- 4 files changed, 41 insertions(+), 10 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3201434da58..020226439b7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6599,7 +6599,7 @@ module ts { result.splice(spliceIndex, 0, signature); } } - + function getSpreadArgumentIndex(args: Expression[]): number { for (let i = 0; i < args.length; i++) { if (args[i].kind === SyntaxKind.SpreadElementExpression) { @@ -7121,10 +7121,10 @@ module ts { } function resolveNewExpression(node: NewExpression, candidatesOutArray: Signature[]): Signature { - if (node.arguments && languageVersion < ScriptTarget.ES6) { + if (node.arguments && languageVersion < ScriptTarget.ES5) { let spreadIndex = getSpreadArgumentIndex(node.arguments); if (spreadIndex >= 0) { - error(node.arguments[spreadIndex], Diagnostics.Spread_operator_in_new_expressions_is_only_available_when_targeting_ECMAScript_6_and_higher); + error(node.arguments[spreadIndex], Diagnostics.Spread_operator_in_new_expressions_is_only_available_when_targeting_ECMAScript_5_and_higher); } } @@ -7142,7 +7142,7 @@ module ts { // If ConstructExpr's apparent type(section 3.8.1) is an object type with one or // more construct signatures, the expression is processed in the same manner as a // function call, but using the construct signatures as the initial set of candidate - // signatures for overload resolution.The result type of the function call becomes + // signatures for overload resolution. The result type of the function call becomes // the result type of the operation. expressionType = getApparentType(expressionType); if (expressionType === unknownType) { diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts index 32dc590eeec..2a852716846 100644 --- a/src/compiler/diagnosticInformationMap.generated.ts +++ b/src/compiler/diagnosticInformationMap.generated.ts @@ -334,7 +334,7 @@ module ts { The_0_operator_cannot_be_applied_to_type_symbol: { code: 2469, category: DiagnosticCategory.Error, key: "The '{0}' operator cannot be applied to type 'symbol'." }, Symbol_reference_does_not_refer_to_the_global_Symbol_constructor_object: { code: 2470, category: DiagnosticCategory.Error, key: "'Symbol' reference does not refer to the global Symbol constructor object." }, A_computed_property_name_of_the_form_0_must_be_of_type_symbol: { code: 2471, category: DiagnosticCategory.Error, key: "A computed property name of the form '{0}' must be of type 'symbol'." }, - Spread_operator_in_new_expressions_is_only_available_when_targeting_ECMAScript_6_and_higher: { code: 2472, category: DiagnosticCategory.Error, key: "Spread operator in 'new' expressions is only available when targeting ECMAScript 6 and higher." }, + Spread_operator_in_new_expressions_is_only_available_when_targeting_ECMAScript_5_and_higher: { code: 2472, category: DiagnosticCategory.Error, key: "Spread operator in 'new' expressions is only available when targeting ECMAScript 5 and higher." }, Enum_declarations_must_all_be_const_or_non_const: { code: 2473, category: DiagnosticCategory.Error, key: "Enum declarations must all be const or non-const." }, In_const_enum_declarations_member_initializer_must_be_constant_expression: { code: 2474, category: DiagnosticCategory.Error, key: "In 'const' enum declarations member initializer must be constant expression." }, const_enums_can_only_be_used_in_property_or_index_access_expressions_or_the_right_hand_side_of_an_import_declaration_or_export_assignment: { code: 2475, category: DiagnosticCategory.Error, key: "'const' enums can only be used in property or index access expressions or the right hand side of an import declaration or export assignment." }, diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index a3dfd62d34e..cf1645b1407 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1324,7 +1324,7 @@ "category": "Error", "code": 2471 }, - "Spread operator in 'new' expressions is only available when targeting ECMAScript 6 and higher.": { + "Spread operator in 'new' expressions is only available when targeting ECMAScript 5 and higher.": { "category": "Error", "code": 2472 }, diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index c9d23ec5cee..8f33cc151e7 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1886,11 +1886,42 @@ var __param = (this && this.__param) || function (paramIndex, decorator) { function emitNewExpression(node: NewExpression) { write("new "); - emit(node.expression); - if (node.arguments) { + + // Spread operator logic can be supported in new expressions in ES5 using a combination + // of Function.prototype.bind() and Function.prototype.apply(). + // + // Example: + // + // var arguments = [1, 2, 3, 4, 5]; + // new Array(...arguments); + // + // Could be transpiled into ES5: + // + // var arguments = [1, 2, 3, 4, 5]; + // new (Function.bind.apply(Array, [void 0].concat(arguments))); + // + // `[void 0]` is the first argument which represents `thisArg` to the bind method above. + // And `thisArg` will be set to the return value of the constructor when instantiated + // with the new operator — regardless of any value we set `thisArg` to. Thus, we set it + // to an undefined, `void 0`. + if (languageVersion === ScriptTarget.ES5 && + node.arguments && + hasSpreadElement(node.arguments)) { + write("("); - emitCommaList(node.arguments); - write(")"); + write("Function.bind.apply("); + emit(node.expression); + write(", [void 0].concat("); + emitListWithSpread(node.arguments, /*multiline*/false, /*trailingComma*/false); + write(")))"); + } + else { + emit(node.expression); + if (node.arguments) { + write("("); + emitCommaList(node.arguments); + write(")"); + } } }