Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(eslint-plugin): add no-redundant-type-constituents rule #4378

Merged
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 3 additions & 2 deletions .cspell.json
Expand Up @@ -71,8 +71,8 @@
"IIFE",
"IIFEs",
"linebreaks",
"markdownlint",
"lzstring",
"markdownlint",
"necroing",
"nocheck",
"nullish",
Expand Down Expand Up @@ -101,14 +101,15 @@
"transpiled",
"transpiles",
"transpiling",
"tsvfs",
"tsconfigs",
"tsutils",
"tsvfs",
"typedef",
"typedefs",
"unfixable",
"unoptimized",
"unprefixed",
"upsert",
"Zacher"
],
"overrides": [
Expand Down
1 change: 1 addition & 0 deletions packages/eslint-plugin/README.md
Expand Up @@ -136,6 +136,7 @@ Pro Tip: For larger codebases you may want to consider splitting our linting int
| [`@typescript-eslint/no-non-null-asserted-optional-chain`](./docs/rules/no-non-null-asserted-optional-chain.md) | Disallows using a non-null assertion after an optional chain expression | :white_check_mark: | | |
| [`@typescript-eslint/no-non-null-assertion`](./docs/rules/no-non-null-assertion.md) | Disallows non-null assertions using the `!` postfix operator | :white_check_mark: | | |
| [`@typescript-eslint/no-parameter-properties`](./docs/rules/no-parameter-properties.md) | Disallow the use of parameter properties in class constructors | | | |
| [`@typescript-eslint/no-no-redundant-type-constituents.md`](./docs/rules/no-redundant-type-constituents.md) | Disallow members of unions and intersections that do nothing or override type information | | | :thought_balloon: |
| [`@typescript-eslint/no-require-imports`](./docs/rules/no-require-imports.md) | Disallows invocation of `require()` | | | |
| [`@typescript-eslint/no-this-alias`](./docs/rules/no-this-alias.md) | Disallow aliasing `this` | :white_check_mark: | | |
| [`@typescript-eslint/no-type-alias`](./docs/rules/no-type-alias.md) | Disallow the use of type aliases | | | |
Expand Down
@@ -0,0 +1,84 @@
# Disallow members of unions and intersections that do nothing or override type information (`no-redundant-type-constituents`)

Some types can override some other types ("constituents") in a union or intersection and/or be overridden by some other types.

## Rule Details

TypeScript's set theory of types includes cases where a constituent type might be useless in the parent union or intersection.

Within `|` unions:

- `any` and `unknown` "override" all other union members
- `never` is dropped from unions in any position except when in a return type position
- primitive types such as `string` "override" any of their literal types such as `""`

Within `&` intersections:

- `any` and `never` "override" all other intersection members
- `unknown` is dropped from intersections
- literal types "override" any primitive types in an intersection
- literal types such as `""` "override" any of their primitive types such as `string`

Examples of code for this rule:

<!--tabs-->

### ❌ Incorrect

```ts
type UnionAny = any | 'foo';
type UnionUnknown = unknown | 'foo';
type UnionNever = never | 'foo';

type UnionBooleanLiteral = boolean | false;
type UnionNumberLiteral = number | 1;
type UnionStringLiteral = string | 'foo';

type IntersectionAny = any & 'foo';
type IntersectionUnknown = string & unknown;
type IntersectionNever = string | never;

type IntersectionBooleanLiteral = boolean & false;
type IntersectionNumberLiteral = number & 1;
type IntersectionStringLiteral = string & 'foo';
```

### ✅ Correct

```ts
type UnionAny = any;
type UnionUnknown = unknown;
type UnionNever = never;

type UnionBooleanLiteral = boolean;
type UnionNumberLiteral = number;
type UnionStringLiteral = string;

type IntersectionAny = any;
type IntersectionUnknown = string;
type IntersectionNever = string;

type IntersectionBooleanLiteral = false;
type IntersectionNumberLiteral = 1;
type IntersectionStringLiteral = 'foo';

type ReturnUnionNever = () => string | never;
```

## Limitations

This rule plays it safe and only works with bottom types, top types, and comparing literal types to primitive types.
It also does not provide an auto-fixer just yet.

## Further Reading

- [Union Types](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#union-types)
- [Intersection Types](https://www.typescriptlang.org/docs/handbook/2/objects.html#intersection-types)
- [Bottom Types](https://en.wikipedia.org/wiki/Bottom_type)
- [Top Types](https://en.wikipedia.org/wiki/Top_type)

## Attributes

- [ ] ✅ Recommended
- [ ] 🔧 Fixable
- [x] 💭 Requires type information
1 change: 1 addition & 0 deletions packages/eslint-plugin/src/configs/all.ts
Expand Up @@ -88,6 +88,7 @@ export = {
'@typescript-eslint/no-parameter-properties': 'error',
'no-redeclare': 'off',
'@typescript-eslint/no-redeclare': 'error',
'@typescript-eslint/no-redundant-type-constituents': 'error',
'@typescript-eslint/no-require-imports': 'error',
'no-restricted-imports': 'off',
'@typescript-eslint/no-restricted-imports': 'error',
Expand Down
2 changes: 2 additions & 0 deletions packages/eslint-plugin/src/rules/index.ts
Expand Up @@ -60,6 +60,7 @@ import noNonNullAssertedOptionalChain from './no-non-null-asserted-optional-chai
import noNonNullAssertion from './no-non-null-assertion';
import noParameterProperties from './no-parameter-properties';
import noRedeclare from './no-redeclare';
import noRedundantTypeConstituents from './no-redundant-type-constituents';
import noRequireImports from './no-require-imports';
import noRestrictedImports from './no-restricted-imports';
import noShadow from './no-shadow';
Expand Down Expand Up @@ -183,6 +184,7 @@ export default {
'no-non-null-assertion': noNonNullAssertion,
'no-parameter-properties': noParameterProperties,
'no-redeclare': noRedeclare,
'no-redundant-type-constituents': noRedundantTypeConstituents,
'no-require-imports': noRequireImports,
'no-restricted-imports': noRestrictedImports,
'no-shadow': noShadow,
Expand Down