diff --git a/packages/eslint-plugin/src/rules/sort-type-constituents.ts b/packages/eslint-plugin/src/rules/sort-type-constituents.ts index 1abeddcf823..848b2ce0722 100644 --- a/packages/eslint-plugin/src/rules/sort-type-constituents.ts +++ b/packages/eslint-plugin/src/rules/sort-type-constituents.ts @@ -2,7 +2,7 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as util from '../util'; -import { getEnumNames } from '../util'; +import { getEnumNames, typeNodeRequiresParentheses } from '../util'; enum Group { conditional = 'conditional', @@ -96,13 +96,6 @@ function getGroup(node: TSESTree.TypeNode): Group { } } -function requiresParentheses(node: TSESTree.TypeNode): boolean { - return ( - node.type === AST_NODE_TYPES.TSFunctionType || - node.type === AST_NODE_TYPES.TSConstructorType - ); -} - export type Options = [ { checkIntersections?: boolean; @@ -226,7 +219,13 @@ export default util.createRule({ const fix: TSESLint.ReportFixFunction = fixer => { const sorted = expectedOrder - .map(t => (requiresParentheses(t.node) ? `(${t.text})` : t.text)) + .map(t => + typeNodeRequiresParentheses(t.node, t.text) || + (node.type === AST_NODE_TYPES.TSIntersectionType && + t.node.type === AST_NODE_TYPES.TSUnionType) + ? `(${t.text})` + : t.text, + ) .join( node.type === AST_NODE_TYPES.TSIntersectionType ? ' & ' : ' | ', ); diff --git a/packages/eslint-plugin/src/rules/sort-type-union-intersection-members.ts b/packages/eslint-plugin/src/rules/sort-type-union-intersection-members.ts index 1fbf91b9ae8..cbfa7a51594 100644 --- a/packages/eslint-plugin/src/rules/sort-type-union-intersection-members.ts +++ b/packages/eslint-plugin/src/rules/sort-type-union-intersection-members.ts @@ -2,7 +2,7 @@ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as util from '../util'; -import { getEnumNames } from '../util'; +import { getEnumNames, typeNodeRequiresParentheses } from '../util'; enum Group { conditional = 'conditional', @@ -96,13 +96,6 @@ function getGroup(node: TSESTree.TypeNode): Group { } } -function requiresParentheses(node: TSESTree.TypeNode): boolean { - return ( - node.type === AST_NODE_TYPES.TSFunctionType || - node.type === AST_NODE_TYPES.TSConstructorType - ); -} - export type Options = [ { checkIntersections?: boolean; @@ -228,7 +221,13 @@ export default util.createRule({ const fix: TSESLint.ReportFixFunction = fixer => { const sorted = expectedOrder - .map(t => (requiresParentheses(t.node) ? `(${t.text})` : t.text)) + .map(t => + typeNodeRequiresParentheses(t.node, t.text) || + (node.type === AST_NODE_TYPES.TSIntersectionType && + t.node.type === AST_NODE_TYPES.TSUnionType) + ? `(${t.text})` + : t.text, + ) .join( node.type === AST_NODE_TYPES.TSIntersectionType ? ' & ' : ' | ', ); diff --git a/packages/eslint-plugin/src/util/misc.ts b/packages/eslint-plugin/src/util/misc.ts index 351c94a6361..fa9c5ccf528 100644 --- a/packages/eslint-plugin/src/util/misc.ts +++ b/packages/eslint-plugin/src/util/misc.ts @@ -203,6 +203,18 @@ function findLastIndex( return -1; } +function typeNodeRequiresParentheses( + node: TSESTree.TypeNode, + text: string, +): boolean { + return ( + node.type === AST_NODE_TYPES.TSFunctionType || + node.type === AST_NODE_TYPES.TSConstructorType || + (node.type === AST_NODE_TYPES.TSUnionType && text.startsWith('|')) || + (node.type === AST_NODE_TYPES.TSIntersectionType && text.startsWith('&')) + ); +} + export { arrayGroupByToMap, arraysAreEqual, @@ -216,6 +228,7 @@ export { isDefinitionFile, MemberNameType, RequireKeys, + typeNodeRequiresParentheses, upperCaseFirst, findLastIndex, }; diff --git a/packages/eslint-plugin/tests/rules/sort-type-constituents.test.ts b/packages/eslint-plugin/tests/rules/sort-type-constituents.test.ts index 2587375060e..1aa6f8a6c9a 100644 --- a/packages/eslint-plugin/tests/rules/sort-type-constituents.test.ts +++ b/packages/eslint-plugin/tests/rules/sort-type-constituents.test.ts @@ -287,6 +287,32 @@ type T = }, ], }, + { + code: `type T = (| A) ${operator} B;`, + output: `type T = B ${operator} (| A);`, + errors: [ + { + messageId: 'notSortedNamed', + data: { + type, + name: 'T', + }, + }, + ], + }, + { + code: `type T = (& A) ${operator} B;`, + output: `type T = B ${operator} (& A);`, + errors: [ + { + messageId: 'notSortedNamed', + data: { + type, + name: 'T', + }, + }, + ], + }, ]; }; @@ -334,5 +360,21 @@ type T = 1 | string | {} | A; ], }, ], - invalid: [...invalid('|'), ...invalid('&')], + invalid: [ + ...invalid('|'), + ...invalid('&'), + { + code: 'type T = (B | C) & A;', + output: `type T = A & (B | C);`, + errors: [ + { + messageId: 'notSortedNamed', + data: { + type: 'Intersection', + name: 'T', + }, + }, + ], + }, + ], }); diff --git a/packages/eslint-plugin/tests/rules/sort-type-union-intersection-members.test.ts b/packages/eslint-plugin/tests/rules/sort-type-union-intersection-members.test.ts index a24959d8b6a..6242d140634 100644 --- a/packages/eslint-plugin/tests/rules/sort-type-union-intersection-members.test.ts +++ b/packages/eslint-plugin/tests/rules/sort-type-union-intersection-members.test.ts @@ -287,6 +287,32 @@ type T = }, ], }, + { + code: `type T = (| A) ${operator} B;`, + output: `type T = B ${operator} (| A);`, + errors: [ + { + messageId: 'notSortedNamed', + data: { + type, + name: 'T', + }, + }, + ], + }, + { + code: `type T = (& A) ${operator} B;`, + output: `type T = B ${operator} (& A);`, + errors: [ + { + messageId: 'notSortedNamed', + data: { + type, + name: 'T', + }, + }, + ], + }, ]; }; @@ -334,5 +360,21 @@ type T = 1 | string | {} | A; ], }, ], - invalid: [...invalid('|'), ...invalid('&')], + invalid: [ + ...invalid('|'), + ...invalid('&'), + { + code: 'type T = (B | C) & A;', + output: `type T = A & (B | C);`, + errors: [ + { + messageId: 'notSortedNamed', + data: { + type: 'Intersection', + name: 'T', + }, + }, + ], + }, + ], });