diff --git a/changelog_unreleased/typescript/10916.md b/changelog_unreleased/typescript/10916.md new file mode 100644 index 000000000..68f41577b --- /dev/null +++ b/changelog_unreleased/typescript/10916.md @@ -0,0 +1,21 @@ +#### Break the LHS of assignments that has complex type parameters (#10916 by @sosukesuzuki) + + +```ts +// Input +const map: Map< + Function, + Map +> = new Map(); + +// Prettier stable +const map: Map> = + new Map(); + +// Prettier main +const map: Map< + Function, + Map +> = new Map(); + +``` diff --git a/src/language-js/print/assignment.js b/src/language-js/print/assignment.js index e9f4d910c..95828da79 100644 --- a/src/language-js/print/assignment.js +++ b/src/language-js/print/assignment.js @@ -136,7 +136,11 @@ function chooseLayout(path, options, print, leftDoc, rightPropertyName) { return "never-break-after-operator"; } - if (isComplexDestructuring(node) || isComplexTypeAliasParams(node)) { + if ( + isComplexDestructuring(node) || + isComplexTypeAliasParams(node) || + hasComplexTypeAnnotation(node) + ) { return "break-lhs"; } @@ -269,6 +273,45 @@ function isTypeAlias(node) { return node.type === "TSTypeAliasDeclaration" || node.type === "TypeAlias"; } +function hasComplexTypeAnnotation(node) { + if (node.type !== "VariableDeclarator") { + return false; + } + const { typeAnnotation } = node.id; + if (!typeAnnotation || !typeAnnotation.typeAnnotation) { + return false; + } + const typeParams = getTypeParametersFromTypeReference( + typeAnnotation.typeAnnotation + ); + return ( + isNonEmptyArray(typeParams) && + typeParams.length > 1 && + typeParams.some( + (param) => + isNonEmptyArray(getTypeParametersFromTypeReference(param)) || + param.type === "TSConditionalType" + ) + ); +} + +function getTypeParametersFromTypeReference(node) { + if ( + isTypeReference(node) && + node.typeParameters && + node.typeParameters.params + ) { + return node.typeParameters.params; + } + return null; +} + +function isTypeReference(node) { + return ( + node.type === "TSTypeReference" || node.type === "GenericTypeAnnotation" + ); +} + /** * A chain with no calls at all or whose calls are all without arguments or with lone short arguments, * excluding chains printed by `printMemberChain` diff --git a/tests/format/flow/assignments/__snapshots__/jsfmt.spec.js.snap b/tests/format/flow/assignments/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000..9d921c9cb --- /dev/null +++ b/tests/format/flow/assignments/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,29 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`issue-10850.js format 1`] = ` +====================================options===================================== +parsers: ["babel-flow", "flow"] +printWidth: 80 + | printWidth +=====================================input====================================== +const map: Map> = + new Map(); + +const map: Map = + new Map(); + +const map: Map> = new Map(); + +=====================================output===================================== +const map: Map< + Function, + Map +> = new Map(); + +const map: Map = + new Map(); + +const map: Map> = new Map(); + +================================================================================ +`; diff --git a/tests/format/flow/assignments/issue-10850.js b/tests/format/flow/assignments/issue-10850.js new file mode 100644 index 000000000..c9408527e --- /dev/null +++ b/tests/format/flow/assignments/issue-10850.js @@ -0,0 +1,7 @@ +const map: Map> = + new Map(); + +const map: Map = + new Map(); + +const map: Map> = new Map(); diff --git a/tests/format/flow/assignments/jsfmt.spec.js b/tests/format/flow/assignments/jsfmt.spec.js new file mode 100644 index 000000000..6da232e65 --- /dev/null +++ b/tests/format/flow/assignments/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname, ["babel-flow", "flow"]); diff --git a/tests/format/typescript/assignment/__snapshots__/jsfmt.spec.js.snap b/tests/format/typescript/assignment/__snapshots__/jsfmt.spec.js.snap index 84ee6556f..5f5eb0b6d 100644 --- a/tests/format/typescript/assignment/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/typescript/assignment/__snapshots__/jsfmt.spec.js.snap @@ -180,6 +180,42 @@ const firestorePersonallyIdentifiablePaths: Array = ================================================================================ `; +exports[`issue-10850.ts format 1`] = ` +====================================options===================================== +parsers: ["typescript"] +printWidth: 80 + | printWidth +=====================================input====================================== +const map: Map> = + new Map(); + +const map: Map = + new Map(); + +const map: Map = + new Map(); + +const map: Map> = new Map(); + +=====================================output===================================== +const map: Map< + Function, + Map +> = new Map(); + +const map: Map< + Function, + Condition extends Foo ? FooFooFoo : BarBarBar +> = new Map(); + +const map: Map = + new Map(); + +const map: Map> = new Map(); + +================================================================================ +`; + exports[`lone-arg.ts format 1`] = ` ====================================options===================================== parsers: ["typescript"] diff --git a/tests/format/typescript/assignment/issue-10850.ts b/tests/format/typescript/assignment/issue-10850.ts new file mode 100644 index 000000000..4064c5b19 --- /dev/null +++ b/tests/format/typescript/assignment/issue-10850.ts @@ -0,0 +1,10 @@ +const map: Map> = + new Map(); + +const map: Map = + new Map(); + +const map: Map = + new Map(); + +const map: Map> = new Map();