diff --git a/changelog_unreleased/typescript/pr-7228.md b/changelog_unreleased/typescript/pr-7228.md new file mode 100644 index 000000000..10139ec1c --- /dev/null +++ b/changelog_unreleased/typescript/pr-7228.md @@ -0,0 +1,18 @@ +#### Fix edge cases of printing index signatures ([#7228](https://github.com/prettier/prettier/pull/7228) by [@cola119](https://github.com/cola119)) + +Even though index signatures without type annotations or with multiple parameters aren't valid TypeScript, the TypeScript parser supports this syntax. In line with the [previous error recovery efforts](https://prettier.io/blog/2019/11/09/1.19.0.html#enable-formatting-even-if-there-are-parse-errors-in-some-cases-6816-by-thorn0-and-the-babel-team), Prettier now makes sure its output still can be parsed in these cases. Previous versions produced unparseable code. + + +```ts +// Input +type A = { [key: string] }; +type B = { [a: string, b: string]: string; }; + +// Prettier stable +type A = { [key: string]: }; +type B = { [a: stringb: string]: string }; + +// Prettier master +type A = { [key: string] }; +type B = { [a: string, b: string]: string }; +``` diff --git a/src/language-js/printer-estree.js b/src/language-js/printer-estree.js index 29a4ab142..0f837cf3f 100644 --- a/src/language-js/printer-estree.js +++ b/src/language-js/printer-estree.js @@ -3241,15 +3241,28 @@ function printPathNoParens(path, options, print, args) { case "TSIndexSignature": { const parent = path.getParentNode(); + const parametersGroup = group( + concat([ + indent( + concat([ + softline, + join(concat([", ", softline]), path.map(print, "parameters")) + ]) + ), + ifBreak(shouldPrintComma(options) ? "," : ""), + softline + ]) + ); + return concat([ n.export ? "export " : "", n.accessibility ? concat([n.accessibility, " "]) : "", n.static ? "static " : "", n.readonly ? "readonly " : "", "[", - n.parameters ? concat(path.map(print, "parameters")) : "", - "]: ", - path.call(print, "typeAnnotation"), + n.parameters ? parametersGroup : "", + n.typeAnnotation ? "]: " : "]", + n.typeAnnotation ? path.call(print, "typeAnnotation") : "", parent.type === "ClassBody" ? semi : "" ]); } diff --git a/tests/typescript_error_recovery/__snapshots__/jsfmt.spec.js.snap b/tests/typescript_error_recovery/__snapshots__/jsfmt.spec.js.snap index 09f0493bb..5ad364911 100644 --- a/tests/typescript_error_recovery/__snapshots__/jsfmt.spec.js.snap +++ b/tests/typescript_error_recovery/__snapshots__/jsfmt.spec.js.snap @@ -50,6 +50,260 @@ class f7<> { ================================================================================ `; +exports[`generic.ts 2`] = ` +====================================options===================================== +parsers: ["typescript"] +printWidth: 80 +trailingComma: "es5" + | printWidth +=====================================input====================================== +f1<>(); + +new f2<>(); + +function f3<>() {} + +class f4 { + constructor<>() {} +} + +const f5 = function<>() {} + +interface f6<> { + test<>(); +} + +class f7<> { + test<>() {} +} + +=====================================output===================================== +f1<>(); + +new f2<>(); + +function f3<>() {} + +class f4 { + constructor<>() {} +} + +const f5 = function<>() {}; + +interface f6<> { + test<>(); +} + +class f7<> { + test<>() {} +} + +================================================================================ +`; + +exports[`generic.ts 3`] = ` +====================================options===================================== +parsers: ["typescript"] +printWidth: 80 +trailingComma: "all" + | printWidth +=====================================input====================================== +f1<>(); + +new f2<>(); + +function f3<>() {} + +class f4 { + constructor<>() {} +} + +const f5 = function<>() {} + +interface f6<> { + test<>(); +} + +class f7<> { + test<>() {} +} + +=====================================output===================================== +f1<>(); + +new f2<>(); + +function f3<>() {} + +class f4 { + constructor<>() {} +} + +const f5 = function<>() {}; + +interface f6<> { + test<>(); +} + +class f7<> { + test<>() {} +} + +================================================================================ +`; + +exports[`index-signature.ts 1`] = ` +====================================options===================================== +parsers: ["typescript"] +printWidth: 80 + | printWidth +=====================================input====================================== +type A = { [key: string] }; + +type TwoParams = { + [a: string, b: string]: string; +}; +type ThreeParams = { + [a: string, b: string, c: string]: string; +}; + +type TooLong = { + [loooooooooooooooooooooooooong: string, looooooooooooooooooooooooooooooooooooooong: string]: string; +} +type TooLong81 = { [loooooooooooooooooooooooooong: string, loooooooooooooooooong: string]: string; } +type TooLong80 = { [loooooooooooooooooooooooooong: string, looooooooooooooooong: string]: string; } + +=====================================output===================================== +type A = { [key: string] }; + +type TwoParams = { + [a: string, b: string]: string; +}; +type ThreeParams = { + [a: string, b: string, c: string]: string; +}; + +type TooLong = { + [ + loooooooooooooooooooooooooong: string, + looooooooooooooooooooooooooooooooooooooong: string + ]: string; +}; +type TooLong81 = { + [ + loooooooooooooooooooooooooong: string, + loooooooooooooooooong: string + ]: string; +}; +type TooLong80 = { + [loooooooooooooooooooooooooong: string, looooooooooooooooong: string]: string; +}; + +================================================================================ +`; + +exports[`index-signature.ts 2`] = ` +====================================options===================================== +parsers: ["typescript"] +printWidth: 80 +trailingComma: "es5" + | printWidth +=====================================input====================================== +type A = { [key: string] }; + +type TwoParams = { + [a: string, b: string]: string; +}; +type ThreeParams = { + [a: string, b: string, c: string]: string; +}; + +type TooLong = { + [loooooooooooooooooooooooooong: string, looooooooooooooooooooooooooooooooooooooong: string]: string; +} +type TooLong81 = { [loooooooooooooooooooooooooong: string, loooooooooooooooooong: string]: string; } +type TooLong80 = { [loooooooooooooooooooooooooong: string, looooooooooooooooong: string]: string; } + +=====================================output===================================== +type A = { [key: string] }; + +type TwoParams = { + [a: string, b: string]: string; +}; +type ThreeParams = { + [a: string, b: string, c: string]: string; +}; + +type TooLong = { + [ + loooooooooooooooooooooooooong: string, + looooooooooooooooooooooooooooooooooooooong: string, + ]: string; +}; +type TooLong81 = { + [ + loooooooooooooooooooooooooong: string, + loooooooooooooooooong: string, + ]: string; +}; +type TooLong80 = { + [loooooooooooooooooooooooooong: string, looooooooooooooooong: string]: string; +}; + +================================================================================ +`; + +exports[`index-signature.ts 3`] = ` +====================================options===================================== +parsers: ["typescript"] +printWidth: 80 +trailingComma: "all" + | printWidth +=====================================input====================================== +type A = { [key: string] }; + +type TwoParams = { + [a: string, b: string]: string; +}; +type ThreeParams = { + [a: string, b: string, c: string]: string; +}; + +type TooLong = { + [loooooooooooooooooooooooooong: string, looooooooooooooooooooooooooooooooooooooong: string]: string; +} +type TooLong81 = { [loooooooooooooooooooooooooong: string, loooooooooooooooooong: string]: string; } +type TooLong80 = { [loooooooooooooooooooooooooong: string, looooooooooooooooong: string]: string; } + +=====================================output===================================== +type A = { [key: string] }; + +type TwoParams = { + [a: string, b: string]: string; +}; +type ThreeParams = { + [a: string, b: string, c: string]: string; +}; + +type TooLong = { + [ + loooooooooooooooooooooooooong: string, + looooooooooooooooooooooooooooooooooooooong: string, + ]: string; +}; +type TooLong81 = { + [ + loooooooooooooooooooooooooong: string, + loooooooooooooooooong: string, + ]: string; +}; +type TooLong80 = { + [loooooooooooooooooooooooooong: string, looooooooooooooooong: string]: string; +}; + +================================================================================ +`; + exports[`jsdoc_only_types.ts 1`] = ` ====================================options===================================== parsers: ["typescript"] @@ -81,3 +335,69 @@ let j: (function(): string) | number; ================================================================================ `; + +exports[`jsdoc_only_types.ts 2`] = ` +====================================options===================================== +parsers: ["typescript"] +printWidth: 80 +trailingComma: "es5" + | printWidth +=====================================input====================================== +let a: *; +function b(x: ?) {} +let c: ?string; +let d: string?; +let e: ?(string | number); +let f: !string; +let g: string!; +let h: !(string | number); +let i: function(): string; +let j: (function(): string) | number; + +=====================================output===================================== +let a: *; +function b(x: ?) {} +let c: ?string; +let d: ?string; +let e: ?(string | number); +let f: !string; +let g: !string; +let h: !(string | number); +let i: function(): string; +let j: (function(): string) | number; + +================================================================================ +`; + +exports[`jsdoc_only_types.ts 3`] = ` +====================================options===================================== +parsers: ["typescript"] +printWidth: 80 +trailingComma: "all" + | printWidth +=====================================input====================================== +let a: *; +function b(x: ?) {} +let c: ?string; +let d: string?; +let e: ?(string | number); +let f: !string; +let g: string!; +let h: !(string | number); +let i: function(): string; +let j: (function(): string) | number; + +=====================================output===================================== +let a: *; +function b(x: ?) {} +let c: ?string; +let d: ?string; +let e: ?(string | number); +let f: !string; +let g: !string; +let h: !(string | number); +let i: function(): string; +let j: (function(): string) | number; + +================================================================================ +`; diff --git a/tests/typescript_error_recovery/index-signature.ts b/tests/typescript_error_recovery/index-signature.ts new file mode 100644 index 000000000..896541357 --- /dev/null +++ b/tests/typescript_error_recovery/index-signature.ts @@ -0,0 +1,14 @@ +type A = { [key: string] }; + +type TwoParams = { + [a: string, b: string]: string; +}; +type ThreeParams = { + [a: string, b: string, c: string]: string; +}; + +type TooLong = { + [loooooooooooooooooooooooooong: string, looooooooooooooooooooooooooooooooooooooong: string]: string; +} +type TooLong81 = { [loooooooooooooooooooooooooong: string, loooooooooooooooooong: string]: string; } +type TooLong80 = { [loooooooooooooooooooooooooong: string, looooooooooooooooong: string]: string; } diff --git a/tests/typescript_error_recovery/jsfmt.spec.js b/tests/typescript_error_recovery/jsfmt.spec.js index 2ea3bb6eb..9ae5dd616 100644 --- a/tests/typescript_error_recovery/jsfmt.spec.js +++ b/tests/typescript_error_recovery/jsfmt.spec.js @@ -1 +1,3 @@ run_spec(__dirname, ["typescript"]); +run_spec(__dirname, ["typescript"], { trailingComma: "es5" }); +run_spec(__dirname, ["typescript"], { trailingComma: "all" });