Skip to content

Commit

Permalink
feat(eslint-plugin): add rule sort-type-union-intersection-members
Browse files Browse the repository at this point in the history
  • Loading branch information
bradzacher committed Jan 3, 2021
1 parent 85c2720 commit ab8195c
Show file tree
Hide file tree
Showing 6 changed files with 703 additions and 0 deletions.
1 change: 1 addition & 0 deletions packages/eslint-plugin/README.md
Expand Up @@ -168,6 +168,7 @@ Pro Tip: For larger codebases you may want to consider splitting our linting int
| [`@typescript-eslint/require-array-sort-compare`](./docs/rules/require-array-sort-compare.md) | Requires `Array#sort` calls to always provide a `compareFunction` | | | :thought_balloon: |
| [`@typescript-eslint/restrict-plus-operands`](./docs/rules/restrict-plus-operands.md) | When adding two variables, operands must both be of type number or of type string | :heavy_check_mark: | | :thought_balloon: |
| [`@typescript-eslint/restrict-template-expressions`](./docs/rules/restrict-template-expressions.md) | Enforce template literal expressions to be of string type | :heavy_check_mark: | | :thought_balloon: |
| [`@typescript-eslint/sort-type-union-intersection-members`](./docs/rules/sort-type-union-intersection-members.md) | Enforces that members of a type union/intersection are sorted alphabetically | | :wrench: | |
| [`@typescript-eslint/strict-boolean-expressions`](./docs/rules/strict-boolean-expressions.md) | Restricts the types allowed in boolean expressions | | | :thought_balloon: |
| [`@typescript-eslint/switch-exhaustiveness-check`](./docs/rules/switch-exhaustiveness-check.md) | Exhaustiveness checking in switch with union type | | | :thought_balloon: |
| [`@typescript-eslint/triple-slash-reference`](./docs/rules/triple-slash-reference.md) | Sets preference level for triple slash directives versus ES6-style import declarations | :heavy_check_mark: | | |
Expand Down
@@ -0,0 +1,144 @@
# Enforces that members of a type union/intersection are sorted alphabetically (`sort-type-union-intersection-members`)

Sorting union (`|`) and intersection (`&`) types can help:

- keep your codebase standardized
- find repeated types
- reduce diff churn

## Rule Details

Sorting within each group is done using the following code:

```ts
const collator = new Intl.Collator('en', {
sensitivity: 'base',
numeric: true,
});

function compare(a, b) {
return collator.compare(a, b) || (a < b ? -1 : a > b ? 1 : 0);
}
```

In other words, the types are sorted alphabetically, case-insensitively and treating numbers like a human would, falling back to character code sorting in case of ties.

Examples of **incorrect** code for this rule:

```ts
type T1 = B | A;

type T2 = { b: string } & { a: string };

type T3 = [1, 2, 4] & [1, 2, 3];

type T4 =
| [1, 2, 4]
| [1, 2, 3]
| { b: string }
| { a: string }
| (() => void)
| (() => string)
| 'b'
| 'a'
| 'b'
| 'a'
| readonly string[]
| readonly number[]
| string[]
| number[]
| B
| A
| string
| any;
```

Examples of **correct** code for this rule:

```ts
type T1 = A | B;

type T2 = { a: string } & { b: string };

type T3 = [1, 2, 3] & [1, 2, 4];

type T4 =
| any
| string
| A
| B
| number[]
| string[]
| readonly number[]
| readonly string[]
| 'a'
| 'b'
| 'a'
| 'b'
| (() => string)
| (() => void)
| { a: string }
| { b: string }
| [1, 2, 3]
| [1, 2, 4];
```

## Options

```ts
type Options = {
// true to check intersection types, false otherwise
checkIntersections?: boolean;
// true to check union types, false otherwise
checkUnions?: boolean;
// the ordering of the groups
groupOrder?: (
| 'conditional'
| 'function'
| 'import'
| 'intersection'
| 'keyword'
| 'literal'
| 'named'
| 'object'
| 'operator'
| 'tuple'
| 'union'
)[];
};

const defaultOptions: Options = {
checkIntersections: true,
checkUnions: true,
groupOrder: [
'named',
'keyword',
'operator',
'literal',
'function',
'import',
'conditional',
'object',
'tuple',
'intersection',
'union',
],
};
```

### `groupOrder`

Each member of the type is placed into a group, and then the rule sorts alphabetically within each group.
The ordering of groups is determined by this option.

- `conditional` - Conditional types (`A extends B ? C : D`)
- `function` - Function and constructor types (`() => void`, `new () => type`)
- `import` - Import types (`import('path')`)
- `intersection` - Intersection types (`A & B`)
- `keyword` - Keyword types (`any`, `string`, etc)
- `literal` - Literal types (`1`, `'b'`, `true`, etc)
- `named` - Named types (`A`, `A['prop']`, `B[]`, `Array<C>`)
- `object` - Object types (`{ a: string }`, `{ [key: string]: number }`)
- `operator` - Operator types (`keyof A`, `typeof B`, `readonly C[]`)
- `tuple` - Tuple types (`[A, B, C]`)
- `union` - Union types (`A | B`)
1 change: 1 addition & 0 deletions packages/eslint-plugin/src/configs/all.ts
Expand Up @@ -140,6 +140,7 @@ export = {
'@typescript-eslint/return-await': 'error',
semi: 'off',
'@typescript-eslint/semi': 'error',
'@typescript-eslint/sort-type-union-intersection-members': 'error',
'space-before-function-paren': 'off',
'@typescript-eslint/space-before-function-paren': 'error',
'space-infix-ops': 'off',
Expand Down
2 changes: 2 additions & 0 deletions packages/eslint-plugin/src/rules/index.ts
Expand Up @@ -102,6 +102,7 @@ import restrictPlusOperands from './restrict-plus-operands';
import restrictTemplateExpressions from './restrict-template-expressions';
import returnAwait from './return-await';
import semi from './semi';
import sortTypeUnionIntersectionMembers from './sort-type-union-intersection-members';
import spaceBeforeFunctionParen from './space-before-function-paren';
import spaceInfixOps from './space-infix-ops';
import strictBooleanExpressions from './strict-boolean-expressions';
Expand Down Expand Up @@ -217,6 +218,7 @@ export default {
'restrict-template-expressions': restrictTemplateExpressions,
'return-await': returnAwait,
semi: semi,
'sort-type-union-intersection-members': sortTypeUnionIntersectionMembers,
'space-before-function-paren': spaceBeforeFunctionParen,
'space-infix-ops': spaceInfixOps,
'strict-boolean-expressions': strictBooleanExpressions,
Expand Down

0 comments on commit ab8195c

Please sign in to comment.