From 00a926cc4e6de8a4cf2b910ea1db35ff3b15e496 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Wed, 7 Jun 2017 15:13:24 -0700 Subject: [PATCH] Fix parameter emit for synthetic function types --- src/compiler/emitter.ts | 26 ++- src/compiler/factory.ts | 2 +- src/harness/unittests/printer.ts | 175 +++++++++++++----- .../printsNodeCorrectly.functionTypes.js | 1 + 4 files changed, 146 insertions(+), 58 deletions(-) create mode 100644 tests/baselines/reference/printerApi/printsNodeCorrectly.functionTypes.js diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 1e6cc3b12db..a788bf6a4d0 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -959,7 +959,7 @@ namespace ts { function emitConstructorType(node: ConstructorTypeNode) { write("new "); emitTypeParameters(node, node.typeParameters); - emitParametersForArrow(node, node.parameters); + emitParameters(node, node.parameters); write(" => "); emit(node.type); } @@ -2283,11 +2283,25 @@ namespace ts { emitList(parentNode, parameters, ListFormat.Parameters); } - function emitParametersForArrow(parentNode: Node, parameters: NodeArray) { - if (parameters && - parameters.length === 1 && - parameters[0].type === undefined && - parameters[0].pos === parentNode.pos) { + function canEmitSimpleArrowHead(parentNode: FunctionTypeNode | ArrowFunction, parameters: NodeArray) { + const parameter = singleOrUndefined(parameters); + return parameter + && parameter.pos === parentNode.pos // may not have parsed tokens between parent and parameter + && !(isArrowFunction(parentNode) && parentNode.type) // arrow function may not have return type annotation + && !some(parentNode.decorators) // parent may not have decorators + && !some(parentNode.modifiers) // parent may not have modifiers + && !some(parentNode.typeParameters) // parent may not have type parameters + && !some(parameter.decorators) // parameter may not have decorators + && !some(parameter.modifiers) // parameter may not have modifiers + && !parameter.dotDotDotToken // parameter may not be rest + && !parameter.questionToken // parameter may not be optional + && !parameter.type // parameter may not have a type annotation + && !parameter.initializer // parameter may not have an initializer + && isIdentifier(parameter.name); // parameter name must be identifier + } + + function emitParametersForArrow(parentNode: FunctionTypeNode | ArrowFunction, parameters: NodeArray) { + if (canEmitSimpleArrowHead(parentNode, parameters)) { emit(parameters[0]); } else { diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 1f9cf91efd0..32244a86792 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -228,7 +228,7 @@ namespace ts { // Signature elements - export function createTypeParameterDeclaration(name: string | Identifier, constraint: TypeNode | undefined, defaultType: TypeNode | undefined) { + export function createTypeParameterDeclaration(name: string | Identifier, constraint?: TypeNode, defaultType?: TypeNode) { const node = createSynthesizedNode(SyntaxKind.TypeParameter) as TypeParameterDeclaration; node.name = asName(name); node.constraint = constraint; diff --git a/src/harness/unittests/printer.ts b/src/harness/unittests/printer.ts index 4bdafe04ba1..23e301f3669 100644 --- a/src/harness/unittests/printer.ts +++ b/src/harness/unittests/printer.ts @@ -81,63 +81,136 @@ namespace ts { describe("printNode", () => { const printsCorrectly = makePrintsCorrectly("printsNodeCorrectly"); - let sourceFile: SourceFile; - before(() => sourceFile = createSourceFile("source.ts", "", ScriptTarget.ES2015)); - // tslint:disable boolean-trivia - const syntheticNode = createClassDeclaration( - undefined, - undefined, - /*name*/ createIdentifier("C"), - undefined, - undefined, - createNodeArray([ - createProperty( - undefined, + printsCorrectly("class", {}, printer => printer.printNode( + EmitHint.Unspecified, + createClassDeclaration( + /*decorators*/ undefined, + /*modifiers*/ undefined, + /*name*/ createIdentifier("C"), + /*typeParameters*/ undefined, + /*heritageClauses*/ undefined, + [createProperty( + /*decorators*/ undefined, createNodeArray([createToken(SyntaxKind.PublicKeyword)]), createIdentifier("prop"), - undefined, - undefined, - undefined - ) - ]) - ); + /*questionToken*/ undefined, + /*type*/ undefined, + /*initializer*/ undefined + )] + ), + createSourceFile("source.ts", "", ScriptTarget.ES2015) + )); + + printsCorrectly("namespaceExportDeclaration", {}, printer => printer.printNode( + EmitHint.Unspecified, + createNamespaceExportDeclaration("B"), + createSourceFile("source.ts", "", ScriptTarget.ES2015) + )); // https://github.com/Microsoft/TypeScript/issues/15971 - const classWithOptionalMethodAndProperty = createClassDeclaration( - undefined, - /* modifiers */ createNodeArray([createToken(SyntaxKind.DeclareKeyword)]), - /* name */ createIdentifier("X"), - undefined, - undefined, - createNodeArray([ - createMethod( - undefined, - undefined, - undefined, - /* name */ createIdentifier("method"), - /* questionToken */ createToken(SyntaxKind.QuestionToken), - undefined, - undefined, - /* type */ createKeywordTypeNode(SyntaxKind.VoidKeyword), - undefined + printsCorrectly("classWithOptionalMethodAndProperty", {}, printer => printer.printNode( + EmitHint.Unspecified, + createClassDeclaration( + /*decorators*/ undefined, + /*modifiers*/ [createToken(SyntaxKind.DeclareKeyword)], + /*name*/ createIdentifier("X"), + /*typeParameters*/ undefined, + /*heritageClauses*/ undefined, + [ + createMethod( + /*decorators*/ undefined, + /*modifiers*/ undefined, + /*asteriskToken*/ undefined, + /*name*/ createIdentifier("method"), + /*questionToken*/ createToken(SyntaxKind.QuestionToken), + /*typeParameters*/ undefined, + [], + /*type*/ createKeywordTypeNode(SyntaxKind.VoidKeyword), + /*body*/ undefined + ), + createProperty( + /*decorators*/ undefined, + /*modifiers*/ undefined, + /*name*/ createIdentifier("property"), + /*questionToken*/ createToken(SyntaxKind.QuestionToken), + /*type*/ createKeywordTypeNode(SyntaxKind.StringKeyword), + /*initializer*/ undefined + ), + ] + ), + createSourceFile("source.ts", "", ScriptTarget.ES2015) + )); + + // https://github.com/Microsoft/TypeScript/issues/15651 + printsCorrectly("functionTypes", {}, printer => printer.printNode( + EmitHint.Unspecified, + createTupleTypeNode([ + createFunctionTypeNode( + /*typeArguments*/ undefined, + [createParameter( + /*decorators*/ undefined, + /*modifiers*/ undefined, + /*dotDotDotToken*/ undefined, + createIdentifier("args") + )], + createKeywordTypeNode(SyntaxKind.AnyKeyword) ), - createProperty( - undefined, - undefined, - /* name */ createIdentifier("property"), - /* questionToken */ createToken(SyntaxKind.QuestionToken), - /* type */ createKeywordTypeNode(SyntaxKind.StringKeyword), - undefined + createFunctionTypeNode( + [createTypeParameterDeclaration("T")], + [createParameter( + /*decorators*/ undefined, + /*modifiers*/ undefined, + /*dotDotDotToken*/ undefined, + createIdentifier("args") + )], + createKeywordTypeNode(SyntaxKind.AnyKeyword) ), - ]) - ); - - // tslint:enable boolean-trivia - printsCorrectly("class", {}, printer => printer.printNode(EmitHint.Unspecified, syntheticNode, sourceFile)); - - printsCorrectly("namespaceExportDeclaration", {}, printer => printer.printNode(EmitHint.Unspecified, createNamespaceExportDeclaration("B"), sourceFile)); - - printsCorrectly("classWithOptionalMethodAndProperty", {}, printer => printer.printNode(EmitHint.Unspecified, classWithOptionalMethodAndProperty, sourceFile)); + createFunctionTypeNode( + /*typeArguments*/ undefined, + [createParameter( + /*decorators*/ undefined, + /*modifiers*/ undefined, + createToken(SyntaxKind.DotDotDotToken), + createIdentifier("args") + )], + createKeywordTypeNode(SyntaxKind.AnyKeyword) + ), + createFunctionTypeNode( + /*typeArguments*/ undefined, + [createParameter( + /*decorators*/ undefined, + /*modifiers*/ undefined, + /*dotDotDotToken*/ undefined, + createIdentifier("args"), + createToken(SyntaxKind.QuestionToken) + )], + createKeywordTypeNode(SyntaxKind.AnyKeyword) + ), + createFunctionTypeNode( + /*typeArguments*/ undefined, + [createParameter( + /*decorators*/ undefined, + /*modifiers*/ undefined, + /*dotDotDotToken*/ undefined, + createIdentifier("args"), + /*questionToken*/ undefined, + createKeywordTypeNode(SyntaxKind.AnyKeyword) + )], + createKeywordTypeNode(SyntaxKind.AnyKeyword) + ), + createFunctionTypeNode( + /*typeArguments*/ undefined, + [createParameter( + /*decorators*/ undefined, + /*modifiers*/ undefined, + /*dotDotDotToken*/ undefined, + createObjectBindingPattern([]) + )], + createKeywordTypeNode(SyntaxKind.AnyKeyword) + ), + ]), + createSourceFile("source.ts", "", ScriptTarget.ES2015) + )); }); }); } diff --git a/tests/baselines/reference/printerApi/printsNodeCorrectly.functionTypes.js b/tests/baselines/reference/printerApi/printsNodeCorrectly.functionTypes.js new file mode 100644 index 00000000000..5bfda3ba7c9 --- /dev/null +++ b/tests/baselines/reference/printerApi/printsNodeCorrectly.functionTypes.js @@ -0,0 +1 @@ +[args => any, (args) => any, (...args) => any, (args?) => any, (args: any) => any, ({}) => any] \ No newline at end of file