Skip to content

Commit d82f1e9

Browse files
authored
fix: normalize parenthesized union and intersection member names
1 parent e3554d6 commit d82f1e9

4 files changed

Lines changed: 110 additions & 1 deletion

File tree

rules/sort-union-types.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import { generatePredefinedGroups } from '../utils/generate-predefined-groups'
3232
import { getEslintDisabledLines } from '../utils/get-eslint-disabled-lines'
3333
import { isNodeEslintDisabled } from '../utils/is-node-eslint-disabled'
3434
import { doesCustomGroupMatch } from '../utils/does-custom-group-match'
35+
import { computeNodeName } from './sort-union-types/compute-node-name'
3536
import { sortNodesByGroups } from '../utils/sort-nodes-by-groups'
3637
import { createEslintRule } from '../utils/create-eslint-rule'
3738
import { reportAllErrors } from '../utils/report-all-errors'
@@ -223,7 +224,10 @@ export function sortUnionOrIntersectionTypes<MessageIds extends string>({
223224
break
224225
}
225226

226-
let name = sourceCode.getText(type)
227+
let name = computeNodeName({
228+
sourceCode,
229+
type,
230+
})
227231

228232
let predefinedGroups = generatePredefinedGroups({
229233
cache: cachedGroupsByModifiersAndSelectors,
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import type { TSESTree } from '@typescript-eslint/types'
2+
import type { TSESLint } from '@typescript-eslint/utils'
3+
4+
import { AST_NODE_TYPES } from '@typescript-eslint/utils'
5+
6+
/**
7+
* Computes the name of a union/intersection member.
8+
*
9+
* Multi-line parenthesized unions/intersections can include leading `|` or `&`
10+
* in raw source text, while formatter output often omits them. To keep sorting
11+
* stable across both forms, the leading separator prefix is normalized.
12+
*
13+
* @param params - Parameters object.
14+
* @param params.sourceCode - ESLint source code object for text extraction.
15+
* @param params.type - Type node represented by the member text.
16+
* @returns Normalized member text used for sorting and matching.
17+
*/
18+
export function computeNodeName({
19+
sourceCode,
20+
type,
21+
}: {
22+
sourceCode: TSESLint.SourceCode
23+
type: TSESTree.TypeNode
24+
}): string {
25+
let name = sourceCode.getText(type)
26+
27+
if (
28+
type.type !== AST_NODE_TYPES.TSUnionType &&
29+
type.type !== AST_NODE_TYPES.TSIntersectionType
30+
) {
31+
return name
32+
}
33+
34+
return name.replace(/^\s*[&|]\s*/u, '')
35+
}

test/rules/sort-intersection-types.test.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3222,6 +3222,41 @@ describe('sort-intersection-types', () => {
32223222
`,
32233223
})
32243224
})
3225+
3226+
it('handles parenthesized unions consistently with prettier', async () => {
3227+
await valid({
3228+
code: dedent`
3229+
type ImportExportType = (
3230+
| ts.ExportDeclaration
3231+
| ts.ImportDeclaration
3232+
) & {
3233+
moduleSpecifier: ts.StringLiteral;
3234+
};
3235+
`,
3236+
options: [options],
3237+
})
3238+
3239+
await invalid({
3240+
output: dedent`
3241+
type ImportExportType = (
3242+
| ts.ExportDeclaration
3243+
| ts.ImportDeclaration
3244+
) & {
3245+
moduleSpecifier: ts.StringLiteral;
3246+
};
3247+
`,
3248+
code: dedent`
3249+
type ImportExportType = {
3250+
moduleSpecifier: ts.StringLiteral;
3251+
} & (
3252+
| ts.ExportDeclaration
3253+
| ts.ImportDeclaration
3254+
);
3255+
`,
3256+
errors: [{ messageId: 'unexpectedIntersectionTypesOrder' }],
3257+
options: [options],
3258+
})
3259+
})
32253260
})
32263261

32273262
describe('line-length', () => {

test/rules/sort-union-types.test.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3231,6 +3231,41 @@ describe('sort-union-types', () => {
32313231
`,
32323232
})
32333233
})
3234+
3235+
it('handles parenthesized intersections consistently with prettier', async () => {
3236+
await valid({
3237+
code: dedent`
3238+
type CombinedType =
3239+
| A
3240+
| (
3241+
& B
3242+
& C
3243+
)
3244+
`,
3245+
options: [options],
3246+
})
3247+
3248+
await invalid({
3249+
output: dedent`
3250+
type CombinedType =
3251+
| A
3252+
| (
3253+
& B
3254+
& C
3255+
)
3256+
`,
3257+
code: dedent`
3258+
type CombinedType =
3259+
| (
3260+
& B
3261+
& C
3262+
)
3263+
| A
3264+
`,
3265+
errors: [{ messageId: 'unexpectedUnionTypesOrder' }],
3266+
options: [options],
3267+
})
3268+
})
32343269
})
32353270

32363271
describe('line-length', () => {

0 commit comments

Comments
 (0)