From aeeaffca3594ce33e1fa1c4082c4600bd3d3f5d5 Mon Sep 17 00:00:00 2001 From: Lucas Azzola Date: Thu, 4 May 2017 01:01:45 +1000 Subject: [PATCH] Fix handling of new keyword and call signatures and improve type parameter formatting (#1491) * fix(typescript): fix handling of new keyword * fix(typescript): fix handling of call signatures * feat(typescript): share type parameter formatting with flow --- .gitignore | 1 + src/printer.js | 145 ++++++++---------- .../classes/__snapshots__/jsfmt.spec.js.snap | 6 +- .../__snapshots__/jsfmt.spec.js.snap | 2 +- .../__snapshots__/jsfmt.spec.js.snap | 2 +- .../call/__snapshots__/jsfmt.spec.js.snap | 18 +++ tests/typescript/custom/call/callSignature.ts | 6 + tests/typescript/custom/call/jsfmt.spec.js | 1 + .../new/__snapshots__/jsfmt.spec.js.snap | 20 +++ tests/typescript/custom/new/jsfmt.spec.js | 1 + tests/typescript/custom/new/newKeyword.ts | 7 + .../__snapshots__/jsfmt.spec.js.snap | 82 ++++++++++ .../callAndConstructSignatureLong.ts | 13 ++ .../custom/typeParameters/functionTypeLong.ts | 9 ++ .../typeParameters/interfaceParamsLong.ts | 5 + .../custom/typeParameters/jsfmt.spec.js | 1 + .../typeParameters/typeParametersLong.ts | 3 + 17 files changed, 240 insertions(+), 82 deletions(-) create mode 100644 tests/typescript/custom/call/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/typescript/custom/call/callSignature.ts create mode 100644 tests/typescript/custom/call/jsfmt.spec.js create mode 100644 tests/typescript/custom/new/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/typescript/custom/new/jsfmt.spec.js create mode 100644 tests/typescript/custom/new/newKeyword.ts create mode 100644 tests/typescript/custom/typeParameters/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/typescript/custom/typeParameters/callAndConstructSignatureLong.ts create mode 100644 tests/typescript/custom/typeParameters/functionTypeLong.ts create mode 100644 tests/typescript/custom/typeParameters/interfaceParamsLong.ts create mode 100644 tests/typescript/custom/typeParameters/jsfmt.spec.js create mode 100644 tests/typescript/custom/typeParameters/typeParametersLong.ts diff --git a/.gitignore b/.gitignore index 05bfba481..2908a6b29 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules npm-debug.log /test.js +/.vscode \ No newline at end of file diff --git a/src/printer.js b/src/printer.js index 6ce7731ce..79e021fc3 100644 --- a/src/printer.js +++ b/src/printer.js @@ -756,6 +756,7 @@ function genericPrintNoParens(path, options, print, args) { // typescript accepts ";" and newlines var separator = isTypeAnnotation ? "," : ","; var fields = []; + var prefix = []; var leftBrace = n.exact ? "{|" : "{"; var rightBrace = n.exact ? "|}" : "}"; var parent = path.getParentNode(0); @@ -763,35 +764,25 @@ function genericPrintNoParens(path, options, print, args) { var propertiesField = isTypeScriptType ? "members" : "properties"; - var prefix = [] if (isTypeAnnotation) { fields.push("indexers", "callProperties"); } - if (isTypeScriptInterfaceDeclaration) { prefix.push( printTypeScriptModifiers(path, options, print), "interface ", path.call(print, "name"), + printTypeParameters(path, options, print, "typeParameters"), + " " + ); + } + if (n.heritageClauses) { + prefix.push( + "extends ", + join(", ", path.map(print, "heritageClauses")), " " ); - - if (n.typeParameters) { - prefix.push( - "<", - join(", ", path.map(print, "typeParameters")), - ">" - ); - } - - if (n.heritageClauses) { - prefix.push( - "extends ", - join(", ", path.map(print, "heritageClauses")), - " " - ); - } } fields.push(propertiesField); @@ -1810,10 +1801,8 @@ function genericPrintNoParens(path, options, print, args) { // with flow they are one `TypeParameterDeclaration` node. if (n.type === 'TSFunctionType' && n.typeParameters) { parts.push( - "<", - join(", ", path.map(print, "typeParameters")), - ">" - ) + printTypeParameters(path, options, print, "typeParameters") + ); } else { parts.push(path.call(print, "typeParameters")); } @@ -2026,31 +2015,8 @@ function genericPrintNoParens(path, options, print, args) { ")" ]); case "TypeParameterDeclaration": - case "TypeParameterInstantiation": { - const shouldInline = - n.params.length === 1 && - (n.params[0].type === "ObjectTypeAnnotation" || - n.params[0].type === "NullableTypeAnnotation"); - - if (shouldInline) { - return concat(["<", join(", ", path.map(print, "params")), ">"]); - } - - return group( - concat([ - "<", - indent( - concat([ - softline, - join(concat([",", line]), path.map(print, "params")) - ]) - ), - ifBreak(shouldPrintComma(options, "all") ? "," : ""), - softline, - ">" - ]) - ); - } + case "TypeParameterInstantiation": + return printTypeParameters(path, options, print, "params"); case "TypeParameter": var variance = getFlowVariance(n, options); @@ -2140,30 +2106,9 @@ function genericPrintNoParens(path, options, print, args) { return concat(parts); case "TSTypeReference": - parts.push(path.call(print, "typeName")) - - if (n.typeArguments) { - parts.push( - "<", - join(", ", path.map(print, "typeArguments")), - ">" - ) - } - - return concat(parts); - case "TSCallSignature": return concat([ - "(", - join(", ", path.map(print, "parameters")), - "): ", - path.call(print, "typeAnnotation") - ]); - case "TSConstructSignature": - return concat([ - "new (", - join(", ", path.map(print, "parameters")), - "): ", - path.call(print, "typeAnnotation") + path.call(print, "typeName"), + printTypeParameters(path, options, print, "typeArguments") ]); case "TSTypeQuery": return concat(["typeof ", path.call(print, "exprName")]); @@ -2199,13 +2144,26 @@ function genericPrintNoParens(path, options, print, args) { path.call(print, "indexType"), "]" ]) + case "TSConstructSignature": case "TSConstructorType": - return concat([ - "new(", + case "TSCallSignature": + if (n.type !== "TSCallSignature") { + parts.push("new "); + } + var isType = n.type === "TSConstructorType"; + parts.push( + printTypeParameters(path, options, print, "typeParameters"), + "(", join(", ", path.map(print, "parameters")), - ") => ", - path.call(print, "typeAnnotation"), - ]) + ")" + ); + if (n.typeAnnotation) { + parts.push( + isType ? " => " : ": ", + path.call(print, "typeAnnotation") + ); + } + return concat(parts); case "TSTypeOperator": return concat([ "keyof ", @@ -2223,13 +2181,13 @@ function genericPrintNoParens(path, options, print, args) { "}" ]) case "TSTypeParameter": - parts.push(path.call(print, "name")) + parts.push(path.call(print, "name")); if (n.constraint) { parts.push( " in ", path.call(print, "constraint") - ) + ); } return concat(parts) @@ -3014,6 +2972,39 @@ function printTypeScriptModifiers(path, options, print) { ]); } +function printTypeParameters(path, options, print, paramsKey) { + const n = path.getValue(); + + // In flow, Foo<> is acceptable. In TypeScript, it's a syntax error. + if (!n[paramsKey] || (n.type.startsWith("TS") && !n[paramsKey].length)) { + return ""; + } + + const shouldInline = + n[paramsKey].length === 1 && + (n[paramsKey][0].type === "ObjectTypeAnnotation" || + n[paramsKey][0].type === "NullableTypeAnnotation"); + + if (shouldInline) { + return concat(["<", join(", ", path.map(print, paramsKey)), ">"]); + } + + return group( + concat([ + "<", + indent( + concat([ + softline, + join(concat([",", line]), path.map(print, paramsKey)) + ]) + ), + ifBreak(shouldPrintComma(options, "all") ? "," : ""), + softline, + ">" + ]) + ); +} + function printClass(path, options, print) { const n = path.getValue(); const parts = []; diff --git a/tests/typescript/conformance/classes/__snapshots__/jsfmt.spec.js.snap b/tests/typescript/conformance/classes/__snapshots__/jsfmt.spec.js.snap index ef2af0050..15557bbf2 100644 --- a/tests/typescript/conformance/classes/__snapshots__/jsfmt.spec.js.snap +++ b/tests/typescript/conformance/classes/__snapshots__/jsfmt.spec.js.snap @@ -134,7 +134,7 @@ class C6 extends Mix(Public, Public2) { ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // @declaration: true -type Constructable = new(...args: any[]) => object; +type Constructable = new (...args: any[]) => object; class Private { constructor(...args: any[]) {} @@ -307,7 +307,7 @@ class Thing3 extends Thing2 { ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // @declaration: true -type Constructor = new(...args: any[]) => T; +type Constructor = new (...args: any[]) => T; class Base { constructor(public x: number, public y: number) {} @@ -437,7 +437,7 @@ const Timestamped = >(Base: CT) => { }; } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -type Constructor = new(...args: any[]) => T; +type Constructor = new (...args: any[]) => T; class Base { constructor(public x: number, public y: number) {} diff --git a/tests/typescript/conformance/classes/classDeclarations/classAbstractKeyword/__snapshots__/jsfmt.spec.js.snap b/tests/typescript/conformance/classes/classDeclarations/classAbstractKeyword/__snapshots__/jsfmt.spec.js.snap index 959001ef2..fa9500576 100644 --- a/tests/typescript/conformance/classes/classDeclarations/classAbstractKeyword/__snapshots__/jsfmt.spec.js.snap +++ b/tests/typescript/conformance/classes/classDeclarations/classAbstractKeyword/__snapshots__/jsfmt.spec.js.snap @@ -45,7 +45,7 @@ AAA = A; AAA = "asdf";~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ abstract class A {} -var AAA: new() => A; +var AAA: new () => A; AAA = A; AAA = "asdf"; diff --git a/tests/typescript/conformance/types/constructorType/__snapshots__/jsfmt.spec.js.snap b/tests/typescript/conformance/types/constructorType/__snapshots__/jsfmt.spec.js.snap index 3961f1fac..7c09f9cee 100644 --- a/tests/typescript/conformance/types/constructorType/__snapshots__/jsfmt.spec.js.snap +++ b/tests/typescript/conformance/types/constructorType/__snapshots__/jsfmt.spec.js.snap @@ -3,6 +3,6 @@ exports[`cunstructorType.ts 1`] = ` var d: new(x: number) => void; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -var d: new(x: number) => void; +var d: new (x: number) => void; `; diff --git a/tests/typescript/custom/call/__snapshots__/jsfmt.spec.js.snap b/tests/typescript/custom/call/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000..2ce795717 --- /dev/null +++ b/tests/typescript/custom/call/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,18 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`callSignature.ts 1`] = ` +interface I { + (); + (): void; + (arg: T); + (arg: T): U; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +interface I { + (), + (): void, + (arg: T), + (arg: T): U +} + +`; diff --git a/tests/typescript/custom/call/callSignature.ts b/tests/typescript/custom/call/callSignature.ts new file mode 100644 index 000000000..54e805f92 --- /dev/null +++ b/tests/typescript/custom/call/callSignature.ts @@ -0,0 +1,6 @@ +interface I { + (); + (): void; + (arg: T); + (arg: T): U; +} diff --git a/tests/typescript/custom/call/jsfmt.spec.js b/tests/typescript/custom/call/jsfmt.spec.js new file mode 100644 index 000000000..bc085c48c --- /dev/null +++ b/tests/typescript/custom/call/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname, { parser: "typescript" }); diff --git a/tests/typescript/custom/new/__snapshots__/jsfmt.spec.js.snap b/tests/typescript/custom/new/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000..a05fc9831 --- /dev/null +++ b/tests/typescript/custom/new/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,20 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`newKeyword.ts 1`] = ` +var x: { y: new () => [T, U] }; + +interface I { + new (x: string); + new (x: string); + new (x: number): number; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var x: { y: new () => [T, U] }; + +interface I { + new (x: string), + new (x: string), + new (x: number): number +} + +`; diff --git a/tests/typescript/custom/new/jsfmt.spec.js b/tests/typescript/custom/new/jsfmt.spec.js new file mode 100644 index 000000000..bc085c48c --- /dev/null +++ b/tests/typescript/custom/new/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname, { parser: "typescript" }); diff --git a/tests/typescript/custom/new/newKeyword.ts b/tests/typescript/custom/new/newKeyword.ts new file mode 100644 index 000000000..58e0940e6 --- /dev/null +++ b/tests/typescript/custom/new/newKeyword.ts @@ -0,0 +1,7 @@ +var x: { y: new () => [T, U] }; + +interface I { + new (x: string); + new (x: string); + new (x: number): number; +} diff --git a/tests/typescript/custom/typeParameters/__snapshots__/jsfmt.spec.js.snap b/tests/typescript/custom/typeParameters/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000..64e1a418d --- /dev/null +++ b/tests/typescript/custom/typeParameters/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,82 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`callAndConstructSignatureLong.ts 1`] = ` +interface Interface { + < + Voila, + InViewHumbleVaudevillianVeteran, + CastVicariouslyAsBothVictimAndVillainByTheVicissitudesOfFate + >(): V; + new < + ThisVisage, + NoMereVeneerOfVanity, + IsAVestigeOfTheVoxPopuliNowVacant, + Vanished + >(): V; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +interface Interface { + < + Voila, + InViewHumbleVaudevillianVeteran, + CastVicariouslyAsBothVictimAndVillainByTheVicissitudesOfFate + >(): V, + new < + ThisVisage, + NoMereVeneerOfVanity, + IsAVestigeOfTheVoxPopuliNowVacant, + Vanished + >(): V +} + +`; + +exports[`functionTypeLong.ts 1`] = ` +type AwkwardlyLongFunctionTypeDefinition = < + GenericTypeNumberOne, + GenericTypeNumberTwo, + GenericTypeNumberThree +>( + arg1: GenericTypeNumberOne, + arg2: GenericTypeNumberTwo, + arg3: GenericTypeNumberThree +) => (GenericTypeNumberOne | GenericTypeNumberTwo | GenericTypeNumberThree); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +type AwkwardlyLongFunctionTypeDefinition = < + GenericTypeNumberOne, + GenericTypeNumberTwo, + GenericTypeNumberThree +>( + arg1: GenericTypeNumberOne, + arg2: GenericTypeNumberTwo, + arg3: GenericTypeNumberThree +) => (GenericTypeNumberOne | GenericTypeNumberTwo | GenericTypeNumberThree); + +`; + +exports[`interfaceParamsLong.ts 1`] = ` +interface ReallyReallyLongName< + TypeArgumentNumberOne, + TypeArgumentNumberTwo, + TypeArgumentNumberThree +> {} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +interface ReallyReallyLongName< + TypeArgumentNumberOne, + TypeArgumentNumberTwo, + TypeArgumentNumberThree +> {} + +`; + +exports[`typeParametersLong.ts 1`] = ` +type ReallyReallyReallyLongName< + ReallyReallyReallyLongName1, ReallyReallyReallyLongName2 +> = any; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +type ReallyReallyReallyLongName< + ReallyReallyReallyLongName1, + ReallyReallyReallyLongName2 +> = any; + +`; diff --git a/tests/typescript/custom/typeParameters/callAndConstructSignatureLong.ts b/tests/typescript/custom/typeParameters/callAndConstructSignatureLong.ts new file mode 100644 index 000000000..962d52d1a --- /dev/null +++ b/tests/typescript/custom/typeParameters/callAndConstructSignatureLong.ts @@ -0,0 +1,13 @@ +interface Interface { + < + Voila, + InViewHumbleVaudevillianVeteran, + CastVicariouslyAsBothVictimAndVillainByTheVicissitudesOfFate + >(): V; + new < + ThisVisage, + NoMereVeneerOfVanity, + IsAVestigeOfTheVoxPopuliNowVacant, + Vanished + >(): V; +} diff --git a/tests/typescript/custom/typeParameters/functionTypeLong.ts b/tests/typescript/custom/typeParameters/functionTypeLong.ts new file mode 100644 index 000000000..6903b7fd8 --- /dev/null +++ b/tests/typescript/custom/typeParameters/functionTypeLong.ts @@ -0,0 +1,9 @@ +type AwkwardlyLongFunctionTypeDefinition = < + GenericTypeNumberOne, + GenericTypeNumberTwo, + GenericTypeNumberThree +>( + arg1: GenericTypeNumberOne, + arg2: GenericTypeNumberTwo, + arg3: GenericTypeNumberThree +) => (GenericTypeNumberOne | GenericTypeNumberTwo | GenericTypeNumberThree); diff --git a/tests/typescript/custom/typeParameters/interfaceParamsLong.ts b/tests/typescript/custom/typeParameters/interfaceParamsLong.ts new file mode 100644 index 000000000..f0ec05b47 --- /dev/null +++ b/tests/typescript/custom/typeParameters/interfaceParamsLong.ts @@ -0,0 +1,5 @@ +interface ReallyReallyLongName< + TypeArgumentNumberOne, + TypeArgumentNumberTwo, + TypeArgumentNumberThree +> {} diff --git a/tests/typescript/custom/typeParameters/jsfmt.spec.js b/tests/typescript/custom/typeParameters/jsfmt.spec.js new file mode 100644 index 000000000..bc085c48c --- /dev/null +++ b/tests/typescript/custom/typeParameters/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname, { parser: "typescript" }); diff --git a/tests/typescript/custom/typeParameters/typeParametersLong.ts b/tests/typescript/custom/typeParameters/typeParametersLong.ts new file mode 100644 index 000000000..f4b4fc2be --- /dev/null +++ b/tests/typescript/custom/typeParameters/typeParametersLong.ts @@ -0,0 +1,3 @@ +type ReallyReallyReallyLongName< + ReallyReallyReallyLongName1, ReallyReallyReallyLongName2 +> = any;