diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index a015ad79b97..58ed2211160 100644 --- a/src/compiler/declarationEmitter.ts +++ b/src/compiler/declarationEmitter.ts @@ -1121,6 +1121,14 @@ namespace ts { writeLine(); } + + function emitSpreadTypeElement(type: SpreadTypeElement) { + write("..."); + emitType(type.type); + write(";"); + writeLine(); + } + function emitVariableDeclaration(node: VariableDeclaration) { // If we are emitting property it isn't moduleElement and hence we already know it needs to be emitted // so there is no check needed to see if declaration is visible @@ -1702,6 +1710,8 @@ namespace ts { case SyntaxKind.PropertyDeclaration: case SyntaxKind.PropertySignature: return emitPropertyDeclaration(node); + case SyntaxKind.SpreadTypeElement: + return emitSpreadTypeElement(node as SpreadTypeElement); case SyntaxKind.EnumMember: return emitEnumMemberDeclaration(node); case SyntaxKind.ExportAssignment: diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 14e743cf59f..cd00f7ad769 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2195,7 +2195,7 @@ const _super = (function (geti, seti) { helpersEmitted = true; } - if (compilerOptions.jsx !== JsxEmit.Preserve && !assignEmitted && (node.flags & NodeFlags.HasJsxSpreadAttributes)) { + if (compilerOptions.jsx !== JsxEmit.Preserve && !assignEmitted && (node.flags & NodeFlags.HasSpreadAttribute)) { writeLines(assignHelper); assignEmitted = true; } diff --git a/src/compiler/transformer.ts b/src/compiler/transformer.ts index 6242a2f2347..2fa57730bf0 100644 --- a/src/compiler/transformer.ts +++ b/src/compiler/transformer.ts @@ -1,6 +1,7 @@ /// /// /// +/// /// /// /// @@ -176,6 +177,7 @@ namespace ts { transformers.push(transformJsx); } + transformers.push(transformExperimental); transformers.push(transformES7); if (languageVersion < ScriptTarget.ES6) { @@ -629,4 +631,4 @@ namespace ts { return t => t; } } -} \ No newline at end of file +} diff --git a/src/compiler/transformers/es7.ts b/src/compiler/transformers/es7.ts index 5d848bc608b..5aaaacb9ecd 100644 --- a/src/compiler/transformers/es7.ts +++ b/src/compiler/transformers/es7.ts @@ -28,7 +28,6 @@ namespace ts { switch (node.kind) { case SyntaxKind.BinaryExpression: return visitBinaryExpression(node); - default: Debug.failBadSyntaxKind(node); return visitEachChild(node, visitor, context); @@ -94,4 +93,4 @@ namespace ts { } } } -} \ No newline at end of file +} diff --git a/src/compiler/transformers/experimental.ts b/src/compiler/transformers/experimental.ts new file mode 100644 index 00000000000..715cf91afd3 --- /dev/null +++ b/src/compiler/transformers/experimental.ts @@ -0,0 +1,80 @@ +/// +/// + +/*@internal*/ +namespace ts { + export function transformExperimental(context: TransformationContext) { + return transformSourceFile; + + function transformSourceFile(node: SourceFile) { + return visitEachChild(node, visitor, context); + } + + function visitor(node: Node): VisitResult { + if (node.transformFlags & TransformFlags.Experimental) { + return visitorWorker(node); + } + else if (node.transformFlags & TransformFlags.ContainsExperimental) { + return visitEachChild(node, visitor, context); + } + else { + return node; + } + } + + function visitorWorker(node: Node): VisitResult { + switch (node.kind) { + case SyntaxKind.ObjectLiteralExpression: + return visitObjectLiteralExpression(node as ObjectLiteralExpression); + default: + Debug.failBadSyntaxKind(node); + return visitEachChild(node, visitor, context); + } + } + + function chunkObjectLiteralElements(elements: ObjectLiteralElement[]): Expression[] { + let chunkObject: (ShorthandPropertyAssignment | PropertyAssignment)[]; + const objects: Expression[] = []; + for (const e of elements) { + if (e.kind === SyntaxKind.SpreadElement) { + if (chunkObject) { + objects.push(createObjectLiteral(chunkObject)); + chunkObject = undefined; + } + const target = (e as SpreadElement).target; + objects.push(visitNode(target, visitor, isExpression)); + } + else { + if (!chunkObject) { + chunkObject = []; + } + if (e.kind === SyntaxKind.PropertyAssignment) { + const p = e as PropertyAssignment; + chunkObject.push(createPropertyAssignment(p.name, visitNode(p.initializer, visitor, isExpression))); + } + else { + chunkObject.push(e as ShorthandPropertyAssignment); + } + } + } + if (chunkObject) { + objects.push(createObjectLiteral(chunkObject)); + } + + return objects; + } + + function visitObjectLiteralExpression(node: ObjectLiteralExpression): Expression { + // spread elements emit like so: + // non-spread elements are chunked together into object literals, and then all are passed to __assign: + // { a, ...o, b } => __assign({a}, o, {b}); + // If the first element is a spread element, then the first argument to __assign is {}: + // { ...o, a, b, ...o2 } => __assign({}, o, {a, b}, o2) + const objects = chunkObjectLiteralElements(node.properties); + if (objects.length && objects[0].kind !== SyntaxKind.ObjectLiteralExpression) { + objects.unshift(createObjectLiteral()); + } + return createCall(createIdentifier("__assign"), undefined, objects); + } + } +}