diff --git a/.cspell.json b/.cspell.json
index 898e10932518..4a8c0ad226be 100644
--- a/.cspell.json
+++ b/.cspell.json
@@ -71,8 +71,8 @@
"IIFE",
"IIFEs",
"linebreaks",
- "markdownlint",
"lzstring",
+ "markdownlint",
"necroing",
"nocheck",
"nullish",
@@ -101,14 +101,15 @@
"transpiled",
"transpiles",
"transpiling",
- "tsvfs",
"tsconfigs",
"tsutils",
+ "tsvfs",
"typedef",
"typedefs",
"unfixable",
"unoptimized",
"unprefixed",
+ "upsert",
"Zacher"
],
"overrides": [
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8c2a68154c59..227285069704 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,28 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [5.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.1...v5.13.0) (2022-02-28)
+
+
+### Bug Fixes
+
+* **eslint-plugin:** [sort-type-union-intersection-members] Wrap the constructorType in parentheses ([#4590](https://github.com/typescript-eslint/typescript-eslint/issues/4590)) ([851bb90](https://github.com/typescript-eslint/typescript-eslint/commit/851bb90216e20b7679efc891dc445e6152d4837f))
+
+
+### Features
+
+* **eslint-plugin:** [no-misused-promises] check more places for checksVoidReturn ([#4541](https://github.com/typescript-eslint/typescript-eslint/issues/4541)) ([052cf51](https://github.com/typescript-eslint/typescript-eslint/commit/052cf51fe663283afe89dc7bf97c947e750df095))
+* **eslint-plugin:** add `no-redundant-type-constituents` rule ([#4378](https://github.com/typescript-eslint/typescript-eslint/issues/4378)) ([63d051e](https://github.com/typescript-eslint/typescript-eslint/commit/63d051eed29dcf71015a23992feac0a8f92717a0))
+* **eslint-plugin:** add `no-useless-empty-export` rule ([#4380](https://github.com/typescript-eslint/typescript-eslint/issues/4380)) ([823b945](https://github.com/typescript-eslint/typescript-eslint/commit/823b945c8f9e83d0246a2a5d07519f01e1a64518))
+* **eslint-plugin:** add extension rule `space-before-blocks` ([#1606](https://github.com/typescript-eslint/typescript-eslint/issues/1606)) ([#4184](https://github.com/typescript-eslint/typescript-eslint/issues/4184)) ([208b6d0](https://github.com/typescript-eslint/typescript-eslint/commit/208b6d02252dff2bf272329d3e4a4a82e56c52c0))
+* **eslint-plugin:** added member group support to member-ordering rule ([#4538](https://github.com/typescript-eslint/typescript-eslint/issues/4538)) ([6afcaea](https://github.com/typescript-eslint/typescript-eslint/commit/6afcaea0160a1ccd1c6483ca677c544ca1b8cb4f))
+* **utils:** expose `ast-utils`' helpers ([#4503](https://github.com/typescript-eslint/typescript-eslint/issues/4503)) ([f106e4b](https://github.com/typescript-eslint/typescript-eslint/commit/f106e4b95e824ebb68141bce3d3207448d50c860))
+* **utils:** extract `isNotTokenOfTypeWithConditions` out of `ast-utils`' `predicates` ([#4502](https://github.com/typescript-eslint/typescript-eslint/issues/4502)) ([66501d6](https://github.com/typescript-eslint/typescript-eslint/commit/66501d6dd7e97c22c671efaa6d1ba8237907e417))
+
+
+
+
+
## [5.12.1](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.0...v5.12.1) (2022-02-21)
diff --git a/README.md b/README.md
index a3535cd83c38..64c2d50e9aeb 100644
--- a/README.md
+++ b/README.md
@@ -37,7 +37,7 @@ All of the packages are published with the same version number to make it easier
We publish a canary release on every successful merge to `main`, so **you never need to wait for a new stable version to make use of any updates**.
-Additionally, we promote the to the `latest` tag on NPM once per week, **on Mondays at 1 pm Eastern**.
+Additionally, we promote to the `latest` tag on NPM once per week, **on Mondays at 1 pm Eastern**.
The latest version under the `latest` tag is:
diff --git a/lerna.json b/lerna.json
index 7adf338d75cc..64055f7146da 100644
--- a/lerna.json
+++ b/lerna.json
@@ -1,5 +1,5 @@
{
- "version": "5.12.1",
+ "version": "5.13.0",
"npmClient": "yarn",
"useWorkspaces": true,
"stream": true
diff --git a/packages/ast-spec/CHANGELOG.md b/packages/ast-spec/CHANGELOG.md
index a34608c74db3..6c09876810e8 100644
--- a/packages/ast-spec/CHANGELOG.md
+++ b/packages/ast-spec/CHANGELOG.md
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [5.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.1...v5.13.0) (2022-02-28)
+
+**Note:** Version bump only for package @typescript-eslint/ast-spec
+
+
+
+
+
## [5.12.1](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.0...v5.12.1) (2022-02-21)
**Note:** Version bump only for package @typescript-eslint/ast-spec
diff --git a/packages/ast-spec/package.json b/packages/ast-spec/package.json
index 230d6657ee1c..6e5f0c3db6b4 100644
--- a/packages/ast-spec/package.json
+++ b/packages/ast-spec/package.json
@@ -1,6 +1,6 @@
{
"name": "@typescript-eslint/ast-spec",
- "version": "5.12.1",
+ "version": "5.13.0",
"description": "TypeScript-ESTree AST spec",
"private": true,
"keywords": [
diff --git a/packages/eslint-plugin-internal/CHANGELOG.md b/packages/eslint-plugin-internal/CHANGELOG.md
index 39e4b11f2610..4cb6c2804ff5 100644
--- a/packages/eslint-plugin-internal/CHANGELOG.md
+++ b/packages/eslint-plugin-internal/CHANGELOG.md
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [5.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.1...v5.13.0) (2022-02-28)
+
+**Note:** Version bump only for package @typescript-eslint/eslint-plugin-internal
+
+
+
+
+
## [5.12.1](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.0...v5.12.1) (2022-02-21)
**Note:** Version bump only for package @typescript-eslint/eslint-plugin-internal
diff --git a/packages/eslint-plugin-internal/package.json b/packages/eslint-plugin-internal/package.json
index b27e1afce240..353fae18f2ef 100644
--- a/packages/eslint-plugin-internal/package.json
+++ b/packages/eslint-plugin-internal/package.json
@@ -1,6 +1,6 @@
{
"name": "@typescript-eslint/eslint-plugin-internal",
- "version": "5.12.1",
+ "version": "5.13.0",
"private": true,
"main": "dist/index.js",
"scripts": {
@@ -14,8 +14,8 @@
},
"dependencies": {
"@types/prettier": "*",
- "@typescript-eslint/scope-manager": "5.12.1",
- "@typescript-eslint/utils": "5.12.1",
+ "@typescript-eslint/scope-manager": "5.13.0",
+ "@typescript-eslint/utils": "5.13.0",
"prettier": "*"
}
}
diff --git a/packages/eslint-plugin-tslint/CHANGELOG.md b/packages/eslint-plugin-tslint/CHANGELOG.md
index d1b858a2d95c..768a35dc57c6 100644
--- a/packages/eslint-plugin-tslint/CHANGELOG.md
+++ b/packages/eslint-plugin-tslint/CHANGELOG.md
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [5.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.1...v5.13.0) (2022-02-28)
+
+**Note:** Version bump only for package @typescript-eslint/eslint-plugin-tslint
+
+
+
+
+
## [5.12.1](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.0...v5.12.1) (2022-02-21)
**Note:** Version bump only for package @typescript-eslint/eslint-plugin-tslint
diff --git a/packages/eslint-plugin-tslint/package.json b/packages/eslint-plugin-tslint/package.json
index 4cd996d123b7..c98cbbbc654d 100644
--- a/packages/eslint-plugin-tslint/package.json
+++ b/packages/eslint-plugin-tslint/package.json
@@ -1,6 +1,6 @@
{
"name": "@typescript-eslint/eslint-plugin-tslint",
- "version": "5.12.1",
+ "version": "5.13.0",
"main": "dist/index.js",
"typings": "src/index.ts",
"description": "TSLint wrapper plugin for ESLint",
@@ -38,7 +38,7 @@
"typecheck": "tsc -p tsconfig.json --noEmit"
},
"dependencies": {
- "@typescript-eslint/utils": "5.12.1",
+ "@typescript-eslint/utils": "5.13.0",
"lodash": "^4.17.21"
},
"peerDependencies": {
@@ -48,6 +48,6 @@
},
"devDependencies": {
"@types/lodash": "*",
- "@typescript-eslint/parser": "5.12.1"
+ "@typescript-eslint/parser": "5.13.0"
}
}
diff --git a/packages/eslint-plugin/CHANGELOG.md b/packages/eslint-plugin/CHANGELOG.md
index 7bba47fd7bce..947c63d50256 100644
--- a/packages/eslint-plugin/CHANGELOG.md
+++ b/packages/eslint-plugin/CHANGELOG.md
@@ -3,6 +3,26 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [5.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.1...v5.13.0) (2022-02-28)
+
+
+### Bug Fixes
+
+* **eslint-plugin:** [sort-type-union-intersection-members] Wrap the constructorType in parentheses ([#4590](https://github.com/typescript-eslint/typescript-eslint/issues/4590)) ([851bb90](https://github.com/typescript-eslint/typescript-eslint/commit/851bb90216e20b7679efc891dc445e6152d4837f))
+
+
+### Features
+
+* **eslint-plugin:** [no-misused-promises] check more places for checksVoidReturn ([#4541](https://github.com/typescript-eslint/typescript-eslint/issues/4541)) ([052cf51](https://github.com/typescript-eslint/typescript-eslint/commit/052cf51fe663283afe89dc7bf97c947e750df095))
+* **eslint-plugin:** add `no-redundant-type-constituents` rule ([#4378](https://github.com/typescript-eslint/typescript-eslint/issues/4378)) ([63d051e](https://github.com/typescript-eslint/typescript-eslint/commit/63d051eed29dcf71015a23992feac0a8f92717a0))
+* **eslint-plugin:** add `no-useless-empty-export` rule ([#4380](https://github.com/typescript-eslint/typescript-eslint/issues/4380)) ([823b945](https://github.com/typescript-eslint/typescript-eslint/commit/823b945c8f9e83d0246a2a5d07519f01e1a64518))
+* **eslint-plugin:** add extension rule `space-before-blocks` ([#1606](https://github.com/typescript-eslint/typescript-eslint/issues/1606)) ([#4184](https://github.com/typescript-eslint/typescript-eslint/issues/4184)) ([208b6d0](https://github.com/typescript-eslint/typescript-eslint/commit/208b6d02252dff2bf272329d3e4a4a82e56c52c0))
+* **eslint-plugin:** added member group support to member-ordering rule ([#4538](https://github.com/typescript-eslint/typescript-eslint/issues/4538)) ([6afcaea](https://github.com/typescript-eslint/typescript-eslint/commit/6afcaea0160a1ccd1c6483ca677c544ca1b8cb4f))
+
+
+
+
+
## [5.12.1](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.0...v5.12.1) (2022-02-21)
diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md
index 47012b81b8fc..34209e621cbd 100644
--- a/packages/eslint-plugin/README.md
+++ b/packages/eslint-plugin/README.md
@@ -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-redundant-type-constituents`](./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 | | | |
@@ -150,6 +151,7 @@ Pro Tip: For larger codebases you may want to consider splitting our linting int
| [`@typescript-eslint/no-unsafe-call`](./docs/rules/no-unsafe-call.md) | Disallows calling an any type value | :white_check_mark: | | :thought_balloon: |
| [`@typescript-eslint/no-unsafe-member-access`](./docs/rules/no-unsafe-member-access.md) | Disallows member access on any typed variables | :white_check_mark: | | :thought_balloon: |
| [`@typescript-eslint/no-unsafe-return`](./docs/rules/no-unsafe-return.md) | Disallows returning any from a function | :white_check_mark: | | :thought_balloon: |
+| [`@typescript-eslint/no-useless-empty-export`](./docs/rules/no-useless-empty-export.md) | Disallow empty exports that don't change anything in a module file | | :wrench: | |
| [`@typescript-eslint/no-var-requires`](./docs/rules/no-var-requires.md) | Disallows the use of require statements except in import statements | :white_check_mark: | | |
| [`@typescript-eslint/non-nullable-type-assertion-style`](./docs/rules/non-nullable-type-assertion-style.md) | Prefers a non-null assertion over explicit type cast when possible | | :wrench: | :thought_balloon: |
| [`@typescript-eslint/prefer-as-const`](./docs/rules/prefer-as-const.md) | Prefer usage of `as const` over literal type | :white_check_mark: | :wrench: | |
@@ -229,6 +231,7 @@ In these cases, we create what we call an extension rule; a rule within our plug
| [`@typescript-eslint/require-await`](./docs/rules/require-await.md) | Disallow async functions which have no `await` expression | :white_check_mark: | | :thought_balloon: |
| [`@typescript-eslint/return-await`](./docs/rules/return-await.md) | Enforces consistent returning of awaited values | | :wrench: | :thought_balloon: |
| [`@typescript-eslint/semi`](./docs/rules/semi.md) | Require or disallow semicolons instead of ASI | | :wrench: | |
+| [`@typescript-eslint/space-before-blocks`](./docs/rules/space-before-blocks.md) | Enforces consistent spacing before blocks | | :wrench: | |
| [`@typescript-eslint/space-before-function-paren`](./docs/rules/space-before-function-paren.md) | Enforces consistent spacing before function parenthesis | | :wrench: | |
| [`@typescript-eslint/space-infix-ops`](./docs/rules/space-infix-ops.md) | This rule is aimed at ensuring there are spaces around infix operators. | | :wrench: | |
diff --git a/packages/eslint-plugin/docs/rules/member-ordering.md b/packages/eslint-plugin/docs/rules/member-ordering.md
index b8e1e9f711fe..ab54967aa3ab 100644
--- a/packages/eslint-plugin/docs/rules/member-ordering.md
+++ b/packages/eslint-plugin/docs/rules/member-ordering.md
@@ -292,6 +292,29 @@ The third grouping option is to ignore both scope and accessibility.
]
```
+### Grouping different member types at the same rank
+
+It is also possible to group different member types at the same rank.
+
+```jsonc
+[
+ // Index signature
+ "signature",
+
+ // Fields
+ "field",
+
+ // Constructors
+ "constructor",
+
+ // Getters and Setters at the same rank
+ ["get", "set"],
+
+ // Methods
+ "method"
+]
+```
+
### Default configuration
The default configuration looks as follows:
diff --git a/packages/eslint-plugin/docs/rules/no-redundant-type-constituents.md b/packages/eslint-plugin/docs/rules/no-redundant-type-constituents.md
new file mode 100644
index 000000000000..e427c671d3fc
--- /dev/null
+++ b/packages/eslint-plugin/docs/rules/no-redundant-type-constituents.md
@@ -0,0 +1,85 @@
+# `no-redundant-type-constituents`
+
+Disallow members of unions and intersections that do nothing or override type information.
+
+## Rule Details
+
+Some types can override some other types ("constituents") in a union or intersection and/or be overridden by some other types.
+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:
+
+
+
+### ❌ 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
diff --git a/packages/eslint-plugin/docs/rules/no-useless-empty-export.md b/packages/eslint-plugin/docs/rules/no-useless-empty-export.md
new file mode 100644
index 000000000000..0cb24763f125
--- /dev/null
+++ b/packages/eslint-plugin/docs/rules/no-useless-empty-export.md
@@ -0,0 +1,45 @@
+# `no-useless-empty-export`
+
+Disallow empty exports that don't change anything in a module file.
+
+## Rule Details
+
+An empty `export {}` statement is sometimes useful in TypeScript code to turn a file that would otherwise be a script file into a module file.
+Per the TypeScript Handbook [Modules](https://www.typescriptlang.org/docs/handbook/modules.html) page:
+
+> In TypeScript, just as in ECMAScript 2015, any file containing a top-level import or export is considered a module.
+> Conversely, a file without any top-level import or export declarations is treated as a script whose contents are available in the global scope (and therefore to modules as well).
+
+However, an `export {}` statement does nothing if there are any other top-level import or export statements in a file.
+
+Examples of code for this rule:
+
+
+
+### ❌ Incorrect
+
+```ts
+export const value = 'Hello, world!';
+export {};
+```
+
+```ts
+import 'some-other-module';
+export {};
+```
+
+### ✅ Correct
+
+```ts
+export const value = 'Hello, world!';
+```
+
+```ts
+import 'some-other-module';
+```
+
+## Attributes
+
+- [ ] ✅ Recommended
+- [x] 🔧 Fixable
+- [ ] 💭 Requires type information
diff --git a/packages/eslint-plugin/docs/rules/space-before-blocks.md b/packages/eslint-plugin/docs/rules/space-before-blocks.md
new file mode 100644
index 000000000000..52f8a121df69
--- /dev/null
+++ b/packages/eslint-plugin/docs/rules/space-before-blocks.md
@@ -0,0 +1,60 @@
+# `space-before-blocks`
+
+Enforces consistent spacing before blocks.
+
+## Rule Details
+
+This rule extends the base [`eslint/space-before-blocks`](https://eslint.org/docs/rules/space-before-blocks) rule.
+It adds support for interfaces and enums:
+
+### ❌ Incorrect
+
+```ts
+enum Breakpoint{
+ Large, Medium;
+}
+
+interface State{
+ currentBreakpoint: Breakpoint;
+}
+```
+
+### ✅ Correct
+
+```ts
+enum Breakpoint {
+ Large, Medium;
+}
+
+interface State {
+ currentBreakpoint: Breakpoint;
+}
+```
+
+In case a more specific options object is passed these blocks will follow `classes` configuration option.
+
+## How to Use
+
+```jsonc
+{
+ // note you must disable the base rule as it can report incorrect errors
+ "space-before-blocks": "off",
+ "@typescript-eslint/space-before-blocks": ["error"]
+}
+```
+
+## Options
+
+See [`eslint/space-before-blocks` options](https://eslint.org/docs/rules/space-before-blocks#options).
+
+
+
+Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/space-before-blocks.md)
+
+
+
+## Attributes
+
+- [ ] ✅ Recommended
+- [x] 🔧 Fixable
+- [ ] 💭 Requires type information
diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json
index 69d7a5529ea7..2ed93a352529 100644
--- a/packages/eslint-plugin/package.json
+++ b/packages/eslint-plugin/package.json
@@ -1,6 +1,6 @@
{
"name": "@typescript-eslint/eslint-plugin",
- "version": "5.12.1",
+ "version": "5.13.0",
"description": "TypeScript plugin for ESLint",
"keywords": [
"eslint",
@@ -44,9 +44,9 @@
"typecheck": "tsc -p tsconfig.json --noEmit"
},
"dependencies": {
- "@typescript-eslint/scope-manager": "5.12.1",
- "@typescript-eslint/type-utils": "5.12.1",
- "@typescript-eslint/utils": "5.12.1",
+ "@typescript-eslint/scope-manager": "5.13.0",
+ "@typescript-eslint/type-utils": "5.13.0",
+ "@typescript-eslint/utils": "5.13.0",
"debug": "^4.3.2",
"functional-red-black-tree": "^1.0.1",
"ignore": "^5.1.8",
diff --git a/packages/eslint-plugin/src/configs/all.ts b/packages/eslint-plugin/src/configs/all.ts
index 538be53ce583..03188b724648 100644
--- a/packages/eslint-plugin/src/configs/all.ts
+++ b/packages/eslint-plugin/src/configs/all.ts
@@ -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',
@@ -114,6 +115,7 @@ export = {
'@typescript-eslint/no-unused-vars': 'error',
'no-use-before-define': 'off',
'@typescript-eslint/no-use-before-define': 'error',
+ '@typescript-eslint/no-useless-empty-export': 'error',
'no-useless-constructor': 'off',
'@typescript-eslint/no-useless-constructor': 'error',
'@typescript-eslint/no-var-requires': 'error',
@@ -155,6 +157,8 @@ export = {
'@typescript-eslint/space-before-function-paren': 'error',
'space-infix-ops': 'off',
'@typescript-eslint/space-infix-ops': 'error',
+ 'space-before-blocks': 'off',
+ '@typescript-eslint/space-before-blocks': 'error',
'@typescript-eslint/strict-boolean-expressions': 'error',
'@typescript-eslint/switch-exhaustiveness-check': 'error',
'@typescript-eslint/triple-slash-reference': 'error',
diff --git a/packages/eslint-plugin/src/rules/index.ts b/packages/eslint-plugin/src/rules/index.ts
index 83e76f0a23d0..c2f90319e386 100644
--- a/packages/eslint-plugin/src/rules/index.ts
+++ b/packages/eslint-plugin/src/rules/index.ts
@@ -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';
@@ -81,6 +82,7 @@ import noUnusedExpressions from './no-unused-expressions';
import noUnusedVars from './no-unused-vars';
import noUseBeforeDefine from './no-use-before-define';
import noUselessConstructor from './no-useless-constructor';
+import noUselessEmptyExport from './no-useless-empty-export';
import noVarRequires from './no-var-requires';
import nonNullableTypeAssertionStyle from './non-nullable-type-assertion-style';
import objectCurlySpacing from './object-curly-spacing';
@@ -110,6 +112,7 @@ import restrictTemplateExpressions from './restrict-template-expressions';
import returnAwait from './return-await';
import semi from './semi';
import sortTypeUnionIntersectionMembers from './sort-type-union-intersection-members';
+import spaceBeforeBlocks from './space-before-blocks';
import spaceBeforeFunctionParen from './space-before-function-paren';
import spaceInfixOps from './space-infix-ops';
import strictBooleanExpressions from './strict-boolean-expressions';
@@ -183,6 +186,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,
@@ -204,6 +208,7 @@ export default {
'no-unused-vars': noUnusedVars,
'no-use-before-define': noUseBeforeDefine,
'no-useless-constructor': noUselessConstructor,
+ 'no-useless-empty-export': noUselessEmptyExport,
'no-var-requires': noVarRequires,
'non-nullable-type-assertion-style': nonNullableTypeAssertionStyle,
'object-curly-spacing': objectCurlySpacing,
@@ -233,6 +238,7 @@ export default {
'return-await': returnAwait,
semi: semi,
'sort-type-union-intersection-members': sortTypeUnionIntersectionMembers,
+ 'space-before-blocks': spaceBeforeBlocks,
'space-before-function-paren': spaceBeforeFunctionParen,
'space-infix-ops': spaceInfixOps,
'strict-boolean-expressions': strictBooleanExpressions,
diff --git a/packages/eslint-plugin/src/rules/member-ordering.ts b/packages/eslint-plugin/src/rules/member-ordering.ts
index ce2edabe058f..92c794caf07f 100644
--- a/packages/eslint-plugin/src/rules/member-ordering.ts
+++ b/packages/eslint-plugin/src/rules/member-ordering.ts
@@ -13,12 +13,14 @@ type Order =
| 'alphabetically-case-insensitive'
| 'as-written';
+type MemberType = string | string[];
+
interface SortedOrderConfig {
- memberTypes?: string[] | 'never';
+ memberTypes?: MemberType[] | 'never';
order: Order;
}
-type OrderConfig = string[] | SortedOrderConfig | 'never';
+type OrderConfig = MemberType[] | SortedOrderConfig | 'never';
type Member = TSESTree.ClassElement | TSESTree.TypeElement;
export type Options = [
@@ -36,14 +38,24 @@ const neverConfig: JSONSchema.JSONSchema4 = {
enum: ['never'],
};
-const arrayConfig = (memberTypes: string[]): JSONSchema.JSONSchema4 => ({
+const arrayConfig = (memberTypes: MemberType[]): JSONSchema.JSONSchema4 => ({
type: 'array',
items: {
- enum: memberTypes,
+ oneOf: [
+ {
+ enum: memberTypes,
+ },
+ {
+ type: 'array',
+ items: {
+ enum: memberTypes,
+ },
+ },
+ ],
},
});
-const objectConfig = (memberTypes: string[]): JSONSchema.JSONSchema4 => ({
+const objectConfig = (memberTypes: MemberType[]): JSONSchema.JSONSchema4 => ({
type: 'object',
properties: {
memberTypes: {
@@ -339,12 +351,20 @@ function getMemberName(
*
* @return Index of the matching member type in the order configuration.
*/
-function getRankOrder(memberGroups: string[], orderConfig: string[]): number {
+function getRankOrder(
+ memberGroups: string[],
+ orderConfig: MemberType[],
+): number {
let rank = -1;
const stack = memberGroups.slice(); // Get a copy of the member groups
while (stack.length > 0 && rank === -1) {
- rank = orderConfig.indexOf(stack.shift()!);
+ const memberGroup = stack.shift()!;
+ rank = orderConfig.findIndex(memberType =>
+ Array.isArray(memberType)
+ ? memberType.includes(memberGroup)
+ : memberType === memberGroup,
+ );
}
return rank;
@@ -358,7 +378,7 @@ function getRankOrder(memberGroups: string[], orderConfig: string[]): number {
*/
function getRank(
node: Member,
- orderConfig: string[],
+ orderConfig: MemberType[],
supportsModifiers: boolean,
): number {
const type = getNodeType(node);
@@ -414,7 +434,7 @@ function getRank(
}
/**
- * Gets the lowest possible rank higher than target.
+ * Gets the lowest possible rank(s) higher than target.
* e.g. given the following order:
* ...
* public-static-method
@@ -427,15 +447,16 @@ function getRank(
* and considering that a public-instance-method has already been declared, so ranks contains
* public-instance-method, then the lowest possible rank for public-static-method is
* public-instance-method.
+ * If a lowest possible rank is a member group, a comma separated list of ranks is returned.
* @param ranks the existing ranks in the object.
* @param target the target rank.
* @param order the current order to be validated.
- * @returns the name of the lowest possible rank without dashes (-).
+ * @returns the name(s) of the lowest possible rank without dashes (-).
*/
function getLowestRank(
ranks: number[],
target: number,
- order: string[],
+ order: MemberType[],
): string {
let lowest = ranks[ranks.length - 1];
@@ -445,7 +466,9 @@ function getLowestRank(
}
});
- return order[lowest].replace(/-/g, ' ');
+ const lowestRank = order[lowest];
+ const lowestRanks = Array.isArray(lowestRank) ? lowestRank : [lowestRank];
+ return lowestRanks.map(rank => rank.replace(/-/g, ' ')).join(', ');
}
export default util.createRule({
@@ -523,7 +546,7 @@ export default util.createRule({
*/
function checkGroupSort(
members: Member[],
- groupOrder: string[],
+ groupOrder: MemberType[],
supportsModifiers: boolean,
): Array | null {
const previousRanks: number[] = [];
diff --git a/packages/eslint-plugin/src/rules/no-misused-promises.ts b/packages/eslint-plugin/src/rules/no-misused-promises.ts
index 6e42e5474361..7f08402334cc 100644
--- a/packages/eslint-plugin/src/rules/no-misused-promises.ts
+++ b/packages/eslint-plugin/src/rules/no-misused-promises.ts
@@ -11,7 +11,15 @@ type Options = [
},
];
-export default util.createRule({
+type MessageId =
+ | 'conditional'
+ | 'voidReturnArgument'
+ | 'voidReturnVariable'
+ | 'voidReturnProperty'
+ | 'voidReturnReturnValue'
+ | 'voidReturnAttribute';
+
+export default util.createRule({
name: 'no-misused-promises',
meta: {
docs: {
@@ -20,8 +28,16 @@ export default util.createRule({
requiresTypeChecking: true,
},
messages: {
- voidReturn:
+ voidReturnArgument:
'Promise returned in function argument where a void return was expected.',
+ voidReturnVariable:
+ 'Promise-returning function provided to variable where a void return was expected.',
+ voidReturnProperty:
+ 'Promise-returning function provided to property where a void return was expected.',
+ voidReturnReturnValue:
+ 'Promise-returning function provided to return value where a void return was expected.',
+ voidReturnAttribute:
+ 'Promise-returning function provided to attribute where a void return was expected.',
conditional: 'Expected non-Promise value in a boolean conditional.',
},
schema: [
@@ -67,6 +83,11 @@ export default util.createRule({
const voidReturnChecks: TSESLint.RuleListener = {
CallExpression: checkArguments,
NewExpression: checkArguments,
+ AssignmentExpression: checkAssignment,
+ VariableDeclarator: checkVariableDeclaration,
+ Property: checkProperty,
+ ReturnStatement: checkReturnStatement,
+ JSXAttribute: checkJSXAttribute,
};
function checkTestConditional(node: {
@@ -130,13 +151,168 @@ export default util.createRule({
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(argument);
if (returnsThenable(checker, tsNode as ts.Expression)) {
context.report({
- messageId: 'voidReturn',
+ messageId: 'voidReturnArgument',
node: argument,
});
}
}
}
+ function checkAssignment(node: TSESTree.AssignmentExpression): void {
+ const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node);
+ const varType = checker.getTypeAtLocation(tsNode.left);
+ if (!isVoidReturningFunctionType(checker, tsNode.left, varType)) {
+ return;
+ }
+
+ if (returnsThenable(checker, tsNode.right)) {
+ context.report({
+ messageId: 'voidReturnVariable',
+ node: node.right,
+ });
+ }
+ }
+
+ function checkVariableDeclaration(node: TSESTree.VariableDeclarator): void {
+ const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node);
+ if (tsNode.initializer === undefined || node.init === null) {
+ return;
+ }
+ const varType = checker.getTypeAtLocation(tsNode.name);
+ if (!isVoidReturningFunctionType(checker, tsNode.initializer, varType)) {
+ return;
+ }
+
+ if (returnsThenable(checker, tsNode.initializer)) {
+ context.report({
+ messageId: 'voidReturnVariable',
+ node: node.init,
+ });
+ }
+ }
+
+ function checkProperty(node: TSESTree.Property): void {
+ const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node);
+ if (ts.isPropertyAssignment(tsNode)) {
+ const contextualType = checker.getContextualType(tsNode.initializer);
+ if (
+ contextualType !== undefined &&
+ isVoidReturningFunctionType(
+ checker,
+ tsNode.initializer,
+ contextualType,
+ ) &&
+ returnsThenable(checker, tsNode.initializer)
+ ) {
+ context.report({
+ messageId: 'voidReturnProperty',
+ node: node.value,
+ });
+ }
+ } else if (ts.isShorthandPropertyAssignment(tsNode)) {
+ const contextualType = checker.getContextualType(tsNode.name);
+ if (
+ contextualType !== undefined &&
+ isVoidReturningFunctionType(checker, tsNode.name, contextualType) &&
+ returnsThenable(checker, tsNode.name)
+ ) {
+ context.report({
+ messageId: 'voidReturnProperty',
+ node: node.value,
+ });
+ }
+ } else if (ts.isMethodDeclaration(tsNode)) {
+ if (ts.isComputedPropertyName(tsNode.name)) {
+ return;
+ }
+ const obj = tsNode.parent;
+
+ // Below condition isn't satisfied unless something goes wrong,
+ // but is needed for type checking.
+ // 'node' does not include class method declaration so 'obj' is
+ // always an object literal expression, but after converting 'node'
+ // to TypeScript AST, its type includes MethodDeclaration which
+ // does include the case of class method declaration.
+ if (!ts.isObjectLiteralExpression(obj)) {
+ return;
+ }
+
+ const objType = checker.getContextualType(obj);
+ if (objType === undefined) {
+ return;
+ }
+ const propertySymbol = checker.getPropertyOfType(
+ objType,
+ tsNode.name.text,
+ );
+ if (propertySymbol === undefined) {
+ return;
+ }
+
+ const contextualType = checker.getTypeOfSymbolAtLocation(
+ propertySymbol,
+ tsNode.name,
+ );
+
+ if (
+ isVoidReturningFunctionType(checker, tsNode.name, contextualType) &&
+ returnsThenable(checker, tsNode)
+ ) {
+ context.report({
+ messageId: 'voidReturnProperty',
+ node: node.value,
+ });
+ }
+ return;
+ }
+ }
+
+ function checkReturnStatement(node: TSESTree.ReturnStatement): void {
+ const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node);
+ if (tsNode.expression === undefined || node.argument === null) {
+ return;
+ }
+ const contextualType = checker.getContextualType(tsNode.expression);
+ if (
+ contextualType !== undefined &&
+ isVoidReturningFunctionType(
+ checker,
+ tsNode.expression,
+ contextualType,
+ ) &&
+ returnsThenable(checker, tsNode.expression)
+ ) {
+ context.report({
+ messageId: 'voidReturnReturnValue',
+ node: node.argument,
+ });
+ }
+ }
+
+ function checkJSXAttribute(node: TSESTree.JSXAttribute): void {
+ const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node);
+ const value = tsNode.initializer;
+ if (
+ node.value === null ||
+ value === undefined ||
+ !ts.isJsxExpression(value) ||
+ value.expression === undefined
+ ) {
+ return;
+ }
+ const contextualType = checker.getContextualType(value);
+ if (
+ contextualType !== undefined &&
+ isVoidReturningFunctionType(checker, value, contextualType) &&
+ returnsThenable(checker, value.expression)
+ ) {
+ context.report({
+ messageId: 'voidReturnAttribute',
+ node: node.value,
+ });
+ }
+ }
+
return {
...(checksConditionals ? conditionalChecks : {}),
...(checksVoidReturn ? voidReturnChecks : {}),
@@ -219,7 +395,6 @@ function voidFunctionParams(
node: ts.CallExpression | ts.NewExpression,
): Set {
const voidReturnIndices = new Set();
- const thenableReturnIndices = new Set();
const type = checker.getTypeAtLocation(node.expression);
for (const subType of tsutils.unionTypeParts(type)) {
@@ -233,36 +408,41 @@ function voidFunctionParams(
parameter,
node.expression,
);
- for (const subType of tsutils.unionTypeParts(type)) {
- for (const signature of subType.getCallSignatures()) {
- const returnType = signature.getReturnType();
- if (tsutils.isTypeFlagSet(returnType, ts.TypeFlags.Void)) {
- voidReturnIndices.add(index);
- } else if (
- tsutils.isThenableType(checker, node.expression, returnType)
- ) {
- thenableReturnIndices.add(index);
- }
- }
+ if (isVoidReturningFunctionType(checker, node.expression, type)) {
+ voidReturnIndices.add(index);
}
}
}
}
- // If a certain positional argument accepts both thenable and void returns,
- // a promise-returning function is valid
- for (const thenable of thenableReturnIndices) {
- voidReturnIndices.delete(thenable);
- }
-
return voidReturnIndices;
}
-// Returns true if the expression is a function that returns a thenable
-function returnsThenable(
+// Returns true if given type is a void-returning function.
+function isVoidReturningFunctionType(
checker: ts.TypeChecker,
- node: ts.Expression,
+ node: ts.Node,
+ type: ts.Type,
): boolean {
+ let hasVoidReturningFunction = false;
+ let hasThenableReturningFunction = false;
+ for (const subType of tsutils.unionTypeParts(type)) {
+ for (const signature of subType.getCallSignatures()) {
+ const returnType = signature.getReturnType();
+ if (tsutils.isTypeFlagSet(returnType, ts.TypeFlags.Void)) {
+ hasVoidReturningFunction = true;
+ } else if (tsutils.isThenableType(checker, node, returnType)) {
+ hasThenableReturningFunction = true;
+ }
+ }
+ }
+ // If a certain positional argument accepts both thenable and void returns,
+ // a promise-returning function is valid
+ return hasVoidReturningFunction && !hasThenableReturningFunction;
+}
+
+// Returns true if the expression is a function that returns a thenable
+function returnsThenable(checker: ts.TypeChecker, node: ts.Node): boolean {
const type = checker.getApparentType(checker.getTypeAtLocation(node));
for (const subType of tsutils.unionTypeParts(type)) {
diff --git a/packages/eslint-plugin/src/rules/no-redundant-type-constituents.ts b/packages/eslint-plugin/src/rules/no-redundant-type-constituents.ts
new file mode 100644
index 000000000000..1ee8c3c49594
--- /dev/null
+++ b/packages/eslint-plugin/src/rules/no-redundant-type-constituents.ts
@@ -0,0 +1,457 @@
+import { TSESTree, AST_NODE_TYPES } from '@typescript-eslint/utils';
+import * as tsutils from 'tsutils';
+import * as ts from 'typescript';
+import * as util from '../util';
+
+const literalToPrimitiveTypeFlags = {
+ [ts.TypeFlags.BigIntLiteral]: ts.TypeFlags.BigInt,
+ [ts.TypeFlags.BooleanLiteral]: ts.TypeFlags.Boolean,
+ [ts.TypeFlags.NumberLiteral]: ts.TypeFlags.Number,
+ [ts.TypeFlags.StringLiteral]: ts.TypeFlags.String,
+ [ts.TypeFlags.TemplateLiteral]: ts.TypeFlags.String,
+} as const;
+
+const literalTypeFlags = [
+ ts.TypeFlags.BigIntLiteral,
+ ts.TypeFlags.BooleanLiteral,
+ ts.TypeFlags.NumberLiteral,
+ ts.TypeFlags.StringLiteral,
+ ts.TypeFlags.TemplateLiteral,
+] as const;
+
+const primitiveTypeFlags = [
+ ts.TypeFlags.BigInt,
+ ts.TypeFlags.Boolean,
+ ts.TypeFlags.Number,
+ ts.TypeFlags.String,
+] as const;
+
+const primitiveTypeFlagNames = {
+ [ts.TypeFlags.BigInt]: 'bigint',
+ [ts.TypeFlags.Boolean]: 'boolean',
+ [ts.TypeFlags.Number]: 'number',
+ [ts.TypeFlags.String]: 'string',
+} as const;
+
+const primitiveTypeFlagTypes = {
+ bigint: ts.TypeFlags.BigIntLiteral,
+ boolean: ts.TypeFlags.BooleanLiteral,
+ number: ts.TypeFlags.NumberLiteral,
+ string: ts.TypeFlags.StringLiteral,
+} as const;
+
+const keywordNodeTypesToTsTypes = new Map([
+ [TSESTree.AST_NODE_TYPES.TSAnyKeyword, ts.TypeFlags.Any],
+ [TSESTree.AST_NODE_TYPES.TSBigIntKeyword, ts.TypeFlags.BigInt],
+ [TSESTree.AST_NODE_TYPES.TSBooleanKeyword, ts.TypeFlags.Boolean],
+ [TSESTree.AST_NODE_TYPES.TSNeverKeyword, ts.TypeFlags.Never],
+ [TSESTree.AST_NODE_TYPES.TSUnknownKeyword, ts.TypeFlags.Unknown],
+ [TSESTree.AST_NODE_TYPES.TSNumberKeyword, ts.TypeFlags.Number],
+ [TSESTree.AST_NODE_TYPES.TSStringKeyword, ts.TypeFlags.String],
+]);
+
+type PrimitiveTypeFlag = typeof primitiveTypeFlags[number];
+
+interface TypeFlagsWithName {
+ typeFlags: ts.TypeFlags;
+ typeName: string;
+}
+
+interface TypeNodeWithValue {
+ literalValue: unknown;
+ typeNode: TSESTree.TypeNode;
+}
+
+function addToMapGroup(
+ map: Map,
+ key: Key,
+ value: Value,
+): void {
+ const existing = map.get(key);
+
+ if (existing) {
+ existing.push(value);
+ } else {
+ map.set(key, [value]);
+ }
+}
+
+function describeLiteralType(type: ts.Type): string {
+ if (type.isStringLiteral()) {
+ return JSON.stringify(type.value);
+ }
+
+ if (type.isLiteral()) {
+ return type.value.toString();
+ }
+
+ if (util.isTypeAnyType(type)) {
+ return 'any';
+ }
+
+ if (util.isTypeNeverType(type)) {
+ return 'never';
+ }
+
+ if (util.isTypeUnknownType(type)) {
+ return 'unknown';
+ }
+
+ if (util.isTypeTemplateLiteralType(type)) {
+ return 'template literal type';
+ }
+
+ if (util.isTypeBigIntLiteralType(type)) {
+ return `${type.value.negative ? '-' : ''}${type.value.base10Value}n`;
+ }
+
+ if (tsutils.isBooleanLiteralType(type, true)) {
+ return 'true';
+ }
+
+ if (tsutils.isBooleanLiteralType(type, false)) {
+ return 'false';
+ }
+
+ return 'literal type';
+}
+
+function describeLiteralTypeNode(typeNode: TSESTree.TypeNode): string {
+ switch (typeNode.type) {
+ case AST_NODE_TYPES.TSAnyKeyword:
+ return 'any';
+ case AST_NODE_TYPES.TSBooleanKeyword:
+ return 'boolean';
+ case AST_NODE_TYPES.TSNeverKeyword:
+ return 'never';
+ case AST_NODE_TYPES.TSNumberKeyword:
+ return 'number';
+ case AST_NODE_TYPES.TSStringKeyword:
+ return 'string';
+ case AST_NODE_TYPES.TSUnknownKeyword:
+ return 'unknown';
+ case AST_NODE_TYPES.TSLiteralType:
+ switch (typeNode.literal.type) {
+ case TSESTree.AST_NODE_TYPES.Literal:
+ switch (typeof typeNode.literal.value) {
+ case 'bigint':
+ return `${typeNode.literal.value < 0 ? '-' : ''}${
+ typeNode.literal.value
+ }n`;
+ case 'string':
+ return JSON.stringify(typeNode.literal.value);
+ default:
+ return `${typeNode.literal.value}`;
+ }
+ case TSESTree.AST_NODE_TYPES.TemplateLiteral:
+ return 'template literal type';
+ }
+ }
+
+ return 'literal type';
+}
+
+function isNodeInsideReturnType(node: TSESTree.TSUnionType): boolean {
+ return !!(
+ node.parent?.type === AST_NODE_TYPES.TSTypeAnnotation &&
+ node.parent.parent &&
+ (util.isFunctionType(node.parent.parent) ||
+ util.isFunction(node.parent.parent))
+ );
+}
+
+/**
+ * @remarks TypeScript stores boolean types as the union false | true, always.
+ */
+function unionTypePartsUnlessBoolean(type: ts.Type): ts.Type[] {
+ return type.isUnion() &&
+ type.types.length === 2 &&
+ tsutils.isBooleanLiteralType(type.types[0], false) &&
+ tsutils.isBooleanLiteralType(type.types[1], true)
+ ? [type]
+ : tsutils.unionTypeParts(type);
+}
+
+export default util.createRule({
+ name: 'no-redundant-type-constituents',
+ meta: {
+ docs: {
+ description:
+ 'Disallow members of unions and intersections that do nothing or override type information',
+ recommended: false,
+ requiresTypeChecking: true,
+ },
+ messages: {
+ literalOverridden: `{{literal}} is overridden by {{primitive}} in this union type.`,
+ primitiveOverridden: `{{primitive}} is overridden by the {{literal}} in this intersection type.`,
+ overridden: `'{{typeName}}' is overridden by other types in this {{container}} type.`,
+ overrides: `'{{typeName}}' overrides all other types in this {{container}} type.`,
+ },
+ schema: [],
+ type: 'suggestion',
+ },
+ defaultOptions: [],
+ create(context) {
+ const parserServices = util.getParserServices(context);
+ const typesCache = new Map();
+
+ function getTypeNodeTypePartFlags(
+ typeNode: TSESTree.TypeNode,
+ ): TypeFlagsWithName[] {
+ const keywordTypeFlags = keywordNodeTypesToTsTypes.get(typeNode.type);
+ if (keywordTypeFlags) {
+ return [
+ {
+ typeFlags: keywordTypeFlags,
+ typeName: describeLiteralTypeNode(typeNode),
+ },
+ ];
+ }
+
+ if (
+ typeNode.type === AST_NODE_TYPES.TSLiteralType &&
+ typeNode.literal.type === AST_NODE_TYPES.Literal
+ ) {
+ return [
+ {
+ typeFlags:
+ primitiveTypeFlagTypes[
+ typeof typeNode.literal
+ .value as keyof typeof primitiveTypeFlagTypes
+ ],
+ typeName: describeLiteralTypeNode(typeNode),
+ },
+ ];
+ }
+
+ if (typeNode.type === AST_NODE_TYPES.TSUnionType) {
+ return typeNode.types.flatMap(getTypeNodeTypePartFlags);
+ }
+
+ const tsNode = parserServices.esTreeNodeToTSNodeMap.get(typeNode);
+ const checker = parserServices.program.getTypeChecker();
+ const nodeType = checker.getTypeAtLocation(tsNode);
+ const typeParts = unionTypePartsUnlessBoolean(nodeType);
+
+ return typeParts.map(typePart => ({
+ typeFlags: typePart.flags,
+ typeName: describeLiteralType(typePart),
+ }));
+ }
+
+ function getTypeNodeTypePartFlagsCached(
+ typeNode: TSESTree.TypeNode,
+ ): TypeFlagsWithName[] {
+ const existing = typesCache.get(typeNode);
+ if (existing) {
+ return existing;
+ }
+
+ const created = getTypeNodeTypePartFlags(typeNode);
+ typesCache.set(typeNode, created);
+ return created;
+ }
+
+ return {
+ 'TSIntersectionType:exit'(node: TSESTree.TSIntersectionType): void {
+ const seenLiteralTypes = new Map();
+ const seenPrimitiveTypes = new Map<
+ PrimitiveTypeFlag,
+ TSESTree.TypeNode[]
+ >();
+
+ function checkIntersectionBottomAndTopTypes(
+ { typeFlags, typeName }: TypeFlagsWithName,
+ typeNode: TSESTree.TypeNode,
+ ): boolean {
+ for (const [messageId, checkFlag] of [
+ ['overrides', ts.TypeFlags.Any],
+ ['overrides', ts.TypeFlags.Never],
+ ['overridden', ts.TypeFlags.Unknown],
+ ] as const) {
+ if (typeFlags === checkFlag) {
+ context.report({
+ data: {
+ container: 'intersection',
+ typeName,
+ },
+ messageId,
+ node: typeNode,
+ });
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ for (const typeNode of node.types) {
+ const typePartFlags = getTypeNodeTypePartFlagsCached(typeNode);
+
+ for (const typePart of typePartFlags) {
+ if (checkIntersectionBottomAndTopTypes(typePart, typeNode)) {
+ continue;
+ }
+
+ for (const literalTypeFlag of literalTypeFlags) {
+ if (typePart.typeFlags === literalTypeFlag) {
+ addToMapGroup(
+ seenLiteralTypes,
+ literalToPrimitiveTypeFlags[literalTypeFlag],
+ typePart.typeName,
+ );
+ break;
+ }
+ }
+
+ for (const primitiveTypeFlag of primitiveTypeFlags) {
+ if (typePart.typeFlags === primitiveTypeFlag) {
+ addToMapGroup(seenPrimitiveTypes, primitiveTypeFlag, typeNode);
+ }
+ }
+ }
+ }
+
+ // For each primitive type of all the seen primitive types,
+ // if there was a literal type seen that overrides it,
+ // report each of the primitive type's type nodes
+ for (const [primitiveTypeFlag, typeNodes] of seenPrimitiveTypes) {
+ const matchedLiteralTypes = seenLiteralTypes.get(primitiveTypeFlag);
+ if (matchedLiteralTypes) {
+ for (const typeNode of typeNodes) {
+ context.report({
+ data: {
+ literal: matchedLiteralTypes.join(' | '),
+ primitive: primitiveTypeFlagNames[primitiveTypeFlag],
+ },
+ messageId: 'primitiveOverridden',
+ node: typeNode,
+ });
+ }
+ }
+ }
+ },
+ 'TSUnionType:exit'(node: TSESTree.TSUnionType): void {
+ const seenLiteralTypes = new Map<
+ PrimitiveTypeFlag,
+ TypeNodeWithValue[]
+ >();
+ const seenPrimitiveTypes = new Set();
+
+ function checkUnionBottomAndTopTypes(
+ { typeFlags, typeName }: TypeFlagsWithName,
+ typeNode: TSESTree.TypeNode,
+ ): boolean {
+ for (const checkFlag of [
+ ts.TypeFlags.Any,
+ ts.TypeFlags.Unknown,
+ ] as const) {
+ if (typeFlags === checkFlag) {
+ context.report({
+ data: {
+ container: 'union',
+ typeName,
+ },
+ messageId: 'overrides',
+ node: typeNode,
+ });
+ return true;
+ }
+ }
+
+ if (
+ typeFlags === ts.TypeFlags.Never &&
+ !isNodeInsideReturnType(node)
+ ) {
+ context.report({
+ data: {
+ container: 'union',
+ typeName: 'never',
+ },
+ messageId: 'overridden',
+ node: typeNode,
+ });
+ return true;
+ }
+
+ return false;
+ }
+
+ for (const typeNode of node.types) {
+ const typePartFlags = getTypeNodeTypePartFlagsCached(typeNode);
+
+ for (const typePart of typePartFlags) {
+ if (checkUnionBottomAndTopTypes(typePart, typeNode)) {
+ continue;
+ }
+
+ for (const literalTypeFlag of literalTypeFlags) {
+ if (typePart.typeFlags === literalTypeFlag) {
+ addToMapGroup(
+ seenLiteralTypes,
+ literalToPrimitiveTypeFlags[literalTypeFlag],
+ {
+ literalValue: typePart.typeName,
+ typeNode,
+ },
+ );
+ break;
+ }
+ }
+
+ for (const primitiveTypeFlag of primitiveTypeFlags) {
+ if ((typePart.typeFlags & primitiveTypeFlag) !== 0) {
+ seenPrimitiveTypes.add(primitiveTypeFlag);
+ }
+ }
+ }
+ }
+
+ interface TypeFlagWithText {
+ literalValue: unknown;
+ primitiveTypeFlag: PrimitiveTypeFlag;
+ }
+
+ const overriddenTypeNodes = new Map<
+ TSESTree.TypeNode,
+ TypeFlagWithText[]
+ >();
+
+ // For each primitive type of all the seen literal types,
+ // if there was a primitive type seen that overrides it,
+ // upsert the literal text and primitive type under the backing type node
+ for (const [primitiveTypeFlag, typeNodesWithText] of seenLiteralTypes) {
+ if (seenPrimitiveTypes.has(primitiveTypeFlag)) {
+ for (const { literalValue, typeNode } of typeNodesWithText) {
+ addToMapGroup(overriddenTypeNodes, typeNode, {
+ literalValue,
+ primitiveTypeFlag,
+ });
+ }
+ }
+ }
+
+ // For each type node that had at least one overridden literal,
+ // group those literals by their primitive type,
+ // then report each primitive type with all its literals
+ for (const [typeNode, typeFlagsWithText] of overriddenTypeNodes) {
+ const grouped = util.arrayGroupByToMap(
+ typeFlagsWithText,
+ pair => pair.primitiveTypeFlag,
+ );
+
+ for (const [primitiveTypeFlag, pairs] of grouped) {
+ context.report({
+ data: {
+ literal: pairs.map(pair => pair.literalValue).join(' | '),
+ primitive: primitiveTypeFlagNames[primitiveTypeFlag],
+ },
+ messageId: 'literalOverridden',
+ node: typeNode,
+ });
+ }
+ }
+ },
+ };
+ },
+});
diff --git a/packages/eslint-plugin/src/rules/no-useless-empty-export.ts b/packages/eslint-plugin/src/rules/no-useless-empty-export.ts
new file mode 100644
index 000000000000..c06c47b8f8e3
--- /dev/null
+++ b/packages/eslint-plugin/src/rules/no-useless-empty-export.ts
@@ -0,0 +1,79 @@
+import { AST_NODE_TYPES, TSESTree } from '@typescript-eslint/utils';
+import * as util from '../util';
+
+function isEmptyExport(
+ node: TSESTree.Node,
+): node is TSESTree.ExportNamedDeclaration {
+ return (
+ node.type === AST_NODE_TYPES.ExportNamedDeclaration &&
+ node.specifiers.length === 0 &&
+ !node.declaration
+ );
+}
+
+const exportOrImportNodeTypes = new Set([
+ AST_NODE_TYPES.ExportAllDeclaration,
+ AST_NODE_TYPES.ExportDefaultDeclaration,
+ AST_NODE_TYPES.ExportNamedDeclaration,
+ AST_NODE_TYPES.ExportSpecifier,
+ AST_NODE_TYPES.ImportDeclaration,
+ AST_NODE_TYPES.TSExportAssignment,
+ AST_NODE_TYPES.TSImportEqualsDeclaration,
+]);
+
+export default util.createRule({
+ name: 'no-useless-empty-export',
+ meta: {
+ docs: {
+ description:
+ "Disallow empty exports that don't change anything in a module file",
+ recommended: false,
+ suggestion: true,
+ },
+ fixable: 'code',
+ hasSuggestions: true,
+ messages: {
+ uselessExport: 'Empty export does nothing and can be removed.',
+ },
+ schema: [],
+ type: 'suggestion',
+ },
+ defaultOptions: [],
+ create(context) {
+ function checkNode(
+ node: TSESTree.Program | TSESTree.TSModuleDeclaration,
+ ): void {
+ if (!Array.isArray(node.body)) {
+ return;
+ }
+
+ let emptyExport: TSESTree.ExportNamedDeclaration | undefined;
+ let foundOtherExport = false;
+
+ for (const statement of node.body) {
+ if (isEmptyExport(statement)) {
+ emptyExport = statement;
+
+ if (foundOtherExport) {
+ break;
+ }
+ } else if (exportOrImportNodeTypes.has(statement.type)) {
+ foundOtherExport = true;
+ }
+ }
+
+ if (emptyExport && foundOtherExport) {
+ context.report({
+ fix: fixer => fixer.remove(emptyExport!),
+ messageId: 'uselessExport',
+ node: emptyExport,
+ });
+ }
+ }
+
+ return {
+ Program: checkNode,
+ TSModuleDeclaration: checkNode,
+ };
+ },
+});
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 fcdaa00152de..fe7f698949c7 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
@@ -85,7 +85,10 @@ function getGroup(node: TSESTree.TypeNode): Group {
}
function requiresParentheses(node: TSESTree.TypeNode): boolean {
- return node.type === AST_NODE_TYPES.TSFunctionType;
+ return (
+ node.type === AST_NODE_TYPES.TSFunctionType ||
+ node.type === AST_NODE_TYPES.TSConstructorType
+ );
}
export type Options = [
diff --git a/packages/eslint-plugin/src/rules/space-before-blocks.ts b/packages/eslint-plugin/src/rules/space-before-blocks.ts
new file mode 100644
index 000000000000..5bf827df96c3
--- /dev/null
+++ b/packages/eslint-plugin/src/rules/space-before-blocks.ts
@@ -0,0 +1,90 @@
+import { TSESTree } from '@typescript-eslint/utils';
+import { getESLintCoreRule } from '../util/getESLintCoreRule';
+import * as util from '../util';
+
+const baseRule = getESLintCoreRule('space-before-blocks');
+
+export type Options = util.InferOptionsTypeFromRule;
+export type MessageIds = util.InferMessageIdsTypeFromRule;
+
+export default util.createRule({
+ name: 'space-before-blocks',
+ meta: {
+ type: 'layout',
+ docs: {
+ description: 'Enforces consistent spacing before blocks',
+ recommended: false,
+ extendsBaseRule: true,
+ },
+ fixable: baseRule.meta.fixable,
+ hasSuggestions: baseRule.meta.hasSuggestions,
+ schema: baseRule.meta.schema,
+ messages: {
+ // @ts-expect-error -- we report on this messageId so we need to ensure it's there in case ESLint changes in future
+ unexpectedSpace: 'Unexpected space before opening brace.',
+ // @ts-expect-error -- we report on this messageId so we need to ensure it's there in case ESLint changes in future
+ missingSpace: 'Missing space before opening brace.',
+ ...baseRule.meta.messages,
+ },
+ },
+ defaultOptions: ['always'],
+ create(context) {
+ const rules = baseRule.create(context);
+ const config = context.options[0];
+ const sourceCode = context.getSourceCode();
+
+ let requireSpace = true;
+
+ if (typeof config === 'object') {
+ requireSpace = config.classes === 'always';
+ } else if (config === 'never') {
+ requireSpace = false;
+ }
+
+ function checkPrecedingSpace(
+ node: TSESTree.Token | TSESTree.TSInterfaceBody,
+ ): void {
+ const precedingToken = sourceCode.getTokenBefore(node);
+ if (precedingToken && util.isTokenOnSameLine(precedingToken, node)) {
+ const hasSpace = sourceCode.isSpaceBetweenTokens(
+ precedingToken,
+ node as TSESTree.Token,
+ );
+
+ if (requireSpace && !hasSpace) {
+ context.report({
+ node,
+ messageId: 'missingSpace',
+ fix(fixer) {
+ return fixer.insertTextBefore(node, ' ');
+ },
+ });
+ } else if (!requireSpace && hasSpace) {
+ context.report({
+ node,
+ messageId: 'unexpectedSpace',
+ fix(fixer) {
+ return fixer.removeRange([
+ precedingToken.range[1],
+ node.range[0],
+ ]);
+ },
+ });
+ }
+ }
+ }
+
+ function checkSpaceAfterEnum(node: TSESTree.TSEnumDeclaration): void {
+ const punctuator = sourceCode.getTokenAfter(node.id);
+ if (punctuator) {
+ checkPrecedingSpace(punctuator);
+ }
+ }
+
+ return {
+ ...rules,
+ TSEnumDeclaration: checkSpaceAfterEnum,
+ TSInterfaceBody: checkPrecedingSpace,
+ };
+ },
+});
diff --git a/packages/eslint-plugin/src/util/getESLintCoreRule.ts b/packages/eslint-plugin/src/util/getESLintCoreRule.ts
index f61bdb0c3094..5ba9ae369659 100644
--- a/packages/eslint-plugin/src/util/getESLintCoreRule.ts
+++ b/packages/eslint-plugin/src/util/getESLintCoreRule.ts
@@ -33,6 +33,7 @@ interface RuleMap {
'prefer-const': typeof import('eslint/lib/rules/prefer-const');
quotes: typeof import('eslint/lib/rules/quotes');
semi: typeof import('eslint/lib/rules/semi');
+ 'space-before-blocks': typeof import('eslint/lib/rules/space-before-blocks');
'space-infix-ops': typeof import('eslint/lib/rules/space-infix-ops');
strict: typeof import('eslint/lib/rules/strict');
}
diff --git a/packages/eslint-plugin/src/util/misc.ts b/packages/eslint-plugin/src/util/misc.ts
index eb62a029b154..8479feb728dd 100644
--- a/packages/eslint-plugin/src/util/misc.ts
+++ b/packages/eslint-plugin/src/util/misc.ts
@@ -19,6 +19,26 @@ function upperCaseFirst(str: string): string {
return str[0].toUpperCase() + str.slice(1);
}
+function arrayGroupByToMap(
+ array: T[],
+ getKey: (item: T) => Key,
+): Map {
+ const groups = new Map();
+
+ for (const item of array) {
+ const key = getKey(item);
+ const existing = groups.get(key);
+
+ if (existing) {
+ existing.push(item);
+ } else {
+ groups.set(key, [item]);
+ }
+ }
+
+ return groups;
+}
+
/** Return true if both parameters are equal. */
type Equal = (a: T, b: T) => boolean;
@@ -148,6 +168,7 @@ function formatWordList(words: string[]): string {
}
export {
+ arrayGroupByToMap,
arraysAreEqual,
Equal,
ExcludeKeys,
diff --git a/packages/eslint-plugin/tests/rules/member-ordering.test.ts b/packages/eslint-plugin/tests/rules/member-ordering.test.ts
index c9c1090c7348..c21272ca510a 100644
--- a/packages/eslint-plugin/tests/rules/member-ordering.test.ts
+++ b/packages/eslint-plugin/tests/rules/member-ordering.test.ts
@@ -1417,6 +1417,74 @@ class Foo {
},
],
},
+ {
+ code: `
+class Foo {
+ A: string;
+ constructor() {}
+ get B() {}
+ set B() {}
+ get C() {}
+ set C() {}
+ D(): void;
+} `,
+ options: [
+ {
+ default: ['field', 'constructor', ['get', 'set'], 'method'],
+ },
+ ],
+ },
+ {
+ code: `
+class Foo {
+ A: string;
+ constructor() {}
+ B(): void;
+} `,
+ options: [
+ {
+ default: ['field', 'constructor', [], 'method'],
+ },
+ ],
+ },
+ {
+ code: `
+class Foo {
+ A: string;
+ constructor() {}
+ @Dec() private B: string;
+ private C(): void;
+ set D() {}
+ E(): void;
+} `,
+ options: [
+ {
+ default: [
+ 'public-field',
+ 'constructor',
+ ['private-decorated-field', 'public-set', 'private-method'],
+ 'public-method',
+ ],
+ },
+ ],
+ },
+ {
+ code: `
+class Foo {
+ A: string;
+ constructor() {}
+ get B() {}
+ get C() {}
+ set B() {}
+ set C() {}
+ D(): void;
+} `,
+ options: [
+ {
+ default: ['field', 'constructor', ['get'], ['set'], 'method'],
+ },
+ ],
+ },
],
invalid: [
{
@@ -3823,6 +3891,94 @@ class Foo {
},
],
},
+ {
+ code: `
+class Foo {
+ A: string;
+ get B() {}
+ constructor() {}
+ set B() {}
+ get C() {}
+ set C() {}
+ D(): void;
+} `,
+ options: [
+ {
+ default: ['field', 'constructor', ['get', 'set'], 'method'],
+ },
+ ],
+ errors: [
+ {
+ messageId: 'incorrectGroupOrder',
+ data: {
+ name: 'constructor',
+ rank: 'get, set',
+ },
+ line: 5,
+ column: 5,
+ },
+ ],
+ },
+ {
+ code: `
+class Foo {
+ A: string;
+ private C(): void;
+ constructor() {}
+ @Dec() private B: string;
+ set D() {}
+ E(): void;
+} `,
+ options: [
+ {
+ default: [
+ 'public-field',
+ 'constructor',
+ ['private-decorated-field', 'public-set', 'private-method'],
+ 'public-method',
+ ],
+ },
+ ],
+ errors: [
+ {
+ messageId: 'incorrectGroupOrder',
+ data: {
+ name: 'constructor',
+ rank: 'private decorated field, public set, private method',
+ },
+ line: 5,
+ column: 5,
+ },
+ ],
+ },
+ {
+ code: `
+class Foo {
+ A: string;
+ constructor() {}
+ get B() {}
+ set B() {}
+ get C() {}
+ set C() {}
+ D(): void;
+} `,
+ options: [
+ {
+ default: ['field', 'constructor', 'get', ['set'], 'method'],
+ },
+ ],
+ errors: [
+ {
+ messageId: 'incorrectGroupOrder',
+ data: {
+ name: 'C',
+ rank: 'set',
+ },
+ line: 7,
+ column: 5,
+ },
+ ],
+ },
],
};
diff --git a/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts b/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts
index 63e429000c03..961a21d4c892 100644
--- a/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts
+++ b/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts
@@ -166,6 +166,92 @@ async function test(p: Promise | undefined) {
}
}
`,
+ `
+let f;
+f = async () => 10;
+ `,
+ `
+let f: () => Promise;
+f = async () => 10;
+const g = async () => 0;
+const h: () => Promise = async () => 10;
+ `,
+ `
+const obj = {
+ f: async () => 10,
+};
+ `,
+ `
+const f = async () => 123;
+const obj = {
+ f,
+};
+ `,
+ `
+const obj = {
+ async f() {
+ return 0;
+ },
+};
+ `,
+ `
+type O = { f: () => Promise; g: () => Promise };
+const g = async () => 0;
+const obj: O = {
+ f: async () => 10,
+ g,
+};
+ `,
+ `
+type O = { f: () => Promise };
+const name = 'f';
+const obj: O = {
+ async [name]() {
+ return 10;
+ },
+};
+ `,
+ `
+const obj: number = {
+ g() {
+ return 10;
+ },
+};
+ `,
+ `
+const obj = {
+ f: async () => 'foo',
+ async g() {
+ return 0;
+ },
+};
+ `,
+ `
+function f() {
+ return async () => 0;
+}
+function g() {
+ return;
+}
+ `,
+ {
+ code: `
+type O = {
+ bool: boolean;
+ func: () => Promise;
+};
+const Component = (obj: O) => null;
+ 10} />;
+ `,
+ filename: 'react.tsx',
+ },
+ {
+ code: `
+const Component: any = () => null;
+ 10} />;
+ `,
+ filename: 'react.tsx',
+ },
],
invalid: [
@@ -265,7 +351,7 @@ if (!Promise.resolve()) {
errors: [
{
line: 2,
- messageId: 'voidReturn',
+ messageId: 'voidReturnArgument',
},
],
},
@@ -279,7 +365,7 @@ new Promise(async (resolve, reject) => {
errors: [
{
line: 2,
- messageId: 'voidReturn',
+ messageId: 'voidReturnArgument',
},
],
},
@@ -296,7 +382,7 @@ fnWithCallback('val', async (err, res) => {
errors: [
{
line: 6,
- messageId: 'voidReturn',
+ messageId: 'voidReturnArgument',
},
],
},
@@ -311,7 +397,7 @@ fnWithCallback('val', (err, res) => Promise.resolve(res));
errors: [
{
line: 6,
- messageId: 'voidReturn',
+ messageId: 'voidReturnArgument',
},
],
},
@@ -332,7 +418,7 @@ fnWithCallback('val', (err, res) => {
errors: [
{
line: 6,
- messageId: 'voidReturn',
+ messageId: 'voidReturnArgument',
},
],
},
@@ -349,7 +435,7 @@ fnWithCallback?.('val', (err, res) => Promise.resolve(res));
errors: [
{
line: 8,
- messageId: 'voidReturn',
+ messageId: 'voidReturnArgument',
},
],
},
@@ -372,7 +458,7 @@ fnWithCallback('val', (err, res) => {
errors: [
{
line: 8,
- messageId: 'voidReturn',
+ messageId: 'voidReturnArgument',
},
],
},
@@ -432,5 +518,174 @@ function test(p: Promise | undefined) {
},
],
},
+ {
+ code: `
+let f: () => void;
+f = async () => {
+ return 3;
+};
+ `,
+ errors: [
+ {
+ line: 3,
+ messageId: 'voidReturnVariable',
+ },
+ ],
+ },
+ {
+ code: `
+const f: () => void = async () => {
+ return 0;
+};
+const g = async () => 1,
+ h: () => void = async () => {};
+ `,
+ errors: [
+ {
+ line: 2,
+ messageId: 'voidReturnVariable',
+ },
+ {
+ line: 6,
+ messageId: 'voidReturnVariable',
+ },
+ ],
+ },
+ {
+ code: `
+const obj: {
+ f?: () => void;
+} = {};
+obj.f = async () => {
+ return 0;
+};
+ `,
+ errors: [
+ {
+ line: 5,
+ messageId: 'voidReturnVariable',
+ },
+ ],
+ },
+ {
+ code: `
+type O = { f: () => void };
+const obj: O = {
+ f: async () => 'foo',
+};
+ `,
+ errors: [
+ {
+ line: 4,
+ messageId: 'voidReturnProperty',
+ },
+ ],
+ },
+ {
+ code: `
+type O = { f: () => void };
+const f = async () => 0;
+const obj: O = {
+ f,
+};
+ `,
+ errors: [
+ {
+ line: 5,
+ messageId: 'voidReturnProperty',
+ },
+ ],
+ },
+ {
+ code: `
+type O = { f: () => void };
+const obj: O = {
+ async f() {
+ return 0;
+ },
+};
+ `,
+ errors: [
+ {
+ line: 4,
+ messageId: 'voidReturnProperty',
+ },
+ ],
+ },
+ {
+ code: `
+type O = { f: () => void; g: () => void; h: () => void };
+function f(): O {
+ const h = async () => 0;
+ return {
+ async f() {
+ return 123;
+ },
+ g: async () => 0,
+ h,
+ };
+}
+ `,
+ errors: [
+ {
+ line: 6,
+ messageId: 'voidReturnProperty',
+ },
+ {
+ line: 9,
+ messageId: 'voidReturnProperty',
+ },
+ {
+ line: 10,
+ messageId: 'voidReturnProperty',
+ },
+ ],
+ },
+ {
+ code: `
+function f(): () => void {
+ return async () => 0;
+}
+ `,
+ errors: [
+ {
+ line: 3,
+ messageId: 'voidReturnReturnValue',
+ },
+ ],
+ },
+ {
+ code: `
+type O = {
+ func: () => void;
+};
+const Component = (obj: O) => null;
+ 0} />;
+ `,
+ filename: 'react.tsx',
+ errors: [
+ {
+ line: 6,
+ messageId: 'voidReturnAttribute',
+ },
+ ],
+ },
+ {
+ code: `
+type O = {
+ func: () => void;
+};
+const g = async () => 'foo';
+const Component = (obj: O) => null;
+;
+ `,
+ filename: 'react.tsx',
+ errors: [
+ {
+ line: 7,
+ messageId: 'voidReturnAttribute',
+ },
+ ],
+ },
],
});
diff --git a/packages/eslint-plugin/tests/rules/no-redundant-type-constituents.test.ts b/packages/eslint-plugin/tests/rules/no-redundant-type-constituents.test.ts
new file mode 100644
index 000000000000..4994459278f7
--- /dev/null
+++ b/packages/eslint-plugin/tests/rules/no-redundant-type-constituents.test.ts
@@ -0,0 +1,787 @@
+import rule from '../../src/rules/no-redundant-type-constituents';
+import { RuleTester, getFixturesRootDir } from '../RuleTester';
+
+const rootDir = getFixturesRootDir();
+const ruleTester = new RuleTester({
+ parserOptions: {
+ ecmaVersion: 2021,
+ tsconfigRootDir: rootDir,
+ project: './tsconfig.json',
+ },
+ parser: '@typescript-eslint/parser',
+});
+
+ruleTester.run('no-redundant-type-constituents', rule, {
+ valid: [
+ `
+ type T = any;
+ type U = T;
+ `,
+ `
+ type T = never;
+ type U = T;
+ `,
+ `
+ type T = 1 | 2;
+ type U = T | 3;
+ type V = U;
+ `,
+ 'type T = () => never;',
+ 'type T = () => never | string;',
+ `
+ type B = never;
+ type T = () => B | string;
+ `,
+ `
+ type B = string;
+ type T = () => B | never;
+ `,
+ 'type T = () => string | never;',
+ 'type T = { (): string | never };',
+ `
+ function _(): string | never {
+ return '';
+ }
+ `,
+ `
+ const _ = (): string | never => {
+ return '';
+ };
+ `,
+ `
+ type B = string;
+ type T = { (): B | never };
+ `,
+ 'type T = { new (): string | never };',
+ `
+ type B = never;
+ type T = { new (): string | B };
+ `,
+ `
+ type B = unknown;
+ type T = B;
+ `,
+ 'type T = bigint;',
+ `
+ type B = bigint;
+ type T = B;
+ `,
+ 'type T = 1n | 2n;',
+ `
+ type B = 1n;
+ type T = B | 2n;
+ `,
+ 'type T = boolean;',
+ `
+ type B = boolean;
+ type T = B;
+ `,
+ 'type T = false | true;',
+ `
+ type B = false;
+ type T = B | true;
+ `,
+ `
+ type B = true;
+ type T = B | false;
+ `,
+ 'type T = number;',
+ `
+ type B = number;
+ type T = B;
+ `,
+ 'type T = 1 | 2;',
+ `
+ type B = 1;
+ type T = B | 2;
+ `,
+ 'type T = 1 | false;',
+ `
+ type B = 1;
+ type T = B | false;
+ `,
+ 'type T = string;',
+ `
+ type B = string;
+ type T = B;
+ `,
+ "type T = 'a' | 'b';",
+ `
+ type B = 'b';
+ type T = 'a' | B;
+ `,
+ `
+ type B = 'a';
+ type T = B | 'b';
+ `,
+ 'type T = bigint | null;',
+ `
+ type B = bigint;
+ type T = B | null;
+ `,
+ 'type T = boolean | null;',
+ `
+ type B = boolean;
+ type T = B | null;
+ `,
+ 'type T = number | null;',
+ `
+ type B = number;
+ type T = B | null;
+ `,
+ 'type T = string | null;',
+ `
+ type B = string;
+ type T = B | null;
+ `,
+ 'type T = bigint & null;',
+ `
+ type B = bigint;
+ type T = B & null;
+ `,
+ 'type T = boolean & null;',
+ `
+ type B = boolean;
+ type T = B & null;
+ `,
+ 'type T = number & null;',
+ `
+ type B = number;
+ type T = B & null;
+ `,
+ 'type T = string & null;',
+ `
+ type B = string;
+ type T = B & null;
+ `,
+ 'type T = `${string}` & null;',
+ `
+ type B = \`\${string}\`;
+ type T = B & null;
+ `,
+ ],
+
+ invalid: [
+ {
+ code: 'type T = number | any;',
+ errors: [
+ {
+ column: 19,
+ data: {
+ container: 'union',
+ typeName: 'any',
+ },
+ messageId: 'overrides',
+ },
+ ],
+ },
+ {
+ code: `
+ type B = number;
+ type T = B | any;
+ `,
+ errors: [
+ {
+ column: 22,
+ data: {
+ container: 'union',
+ typeName: 'any',
+ },
+ messageId: 'overrides',
+ },
+ ],
+ },
+ {
+ code: 'type T = any | number;',
+ errors: [
+ {
+ column: 10,
+ data: {
+ container: 'union',
+ typeName: 'any',
+ },
+ messageId: 'overrides',
+ },
+ ],
+ },
+ {
+ code: `
+ type B = any;
+ type T = B | number;
+ `,
+ errors: [
+ {
+ column: 18,
+ data: {
+ container: 'union',
+ typeName: 'any',
+ },
+ messageId: 'overrides',
+ },
+ ],
+ },
+ {
+ code: 'type T = number | never;',
+ errors: [
+ {
+ column: 19,
+ data: {
+ container: 'union',
+ typeName: 'never',
+ },
+ messageId: 'overridden',
+ },
+ ],
+ },
+ {
+ code: `
+ type B = number;
+ type T = B | never;
+ `,
+ errors: [
+ {
+ column: 22,
+ data: {
+ container: 'union',
+ typeName: 'never',
+ },
+ messageId: 'overridden',
+ },
+ ],
+ },
+ {
+ code: `
+ type B = never;
+ type T = B | number;
+ `,
+ errors: [
+ {
+ column: 18,
+ data: {
+ container: 'union',
+ typeName: 'never',
+ },
+ messageId: 'overridden',
+ },
+ ],
+ },
+ {
+ code: 'type T = never | number;',
+ errors: [
+ {
+ column: 10,
+ data: {
+ container: 'union',
+ typeName: 'never',
+ },
+ messageId: 'overridden',
+ },
+ ],
+ },
+ {
+ code: 'type T = number | unknown;',
+ errors: [
+ {
+ column: 19,
+ data: {
+ container: 'union',
+ typeName: 'unknown',
+ },
+ messageId: 'overrides',
+ },
+ ],
+ },
+ {
+ code: 'type T = unknown | number;',
+ errors: [
+ {
+ column: 10,
+ data: {
+ container: 'union',
+ typeName: 'unknown',
+ },
+ messageId: 'overrides',
+ },
+ ],
+ },
+ {
+ code: 'type T = number | 0;',
+ errors: [
+ {
+ column: 19,
+ data: {
+ literal: '0',
+ primitive: 'number',
+ },
+ messageId: 'literalOverridden',
+ },
+ ],
+ },
+ {
+ code: 'type T = number | (0 | 1);',
+ errors: [
+ {
+ column: 20,
+ data: {
+ literal: '0 | 1',
+ primitive: 'number',
+ },
+ messageId: 'literalOverridden',
+ },
+ ],
+ },
+ {
+ code: 'type T = (0 | 0) | number;',
+ errors: [
+ {
+ column: 11,
+ data: {
+ literal: '0 | 0',
+ primitive: 'number',
+ },
+ messageId: 'literalOverridden',
+ },
+ ],
+ },
+ {
+ code: `
+ type B = 0 | 1;
+ type T = (2 | B) | number;
+ `,
+ errors: [
+ {
+ column: 19,
+ data: {
+ literal: '2 | 0 | 1',
+ primitive: 'number',
+ },
+ messageId: 'literalOverridden',
+ },
+ ],
+ },
+ {
+ code: 'type T = (0 | (1 | 2)) | number;',
+ errors: [
+ {
+ column: 11,
+ data: {
+ literal: '0 | 1 | 2',
+ primitive: 'number',
+ },
+ messageId: 'literalOverridden',
+ },
+ ],
+ },
+ {
+ code: 'type T = (0 | 1) | number;',
+ errors: [
+ {
+ column: 11,
+ data: {
+ literal: '0 | 1',
+ primitive: 'number',
+ },
+ messageId: 'literalOverridden',
+ },
+ ],
+ },
+ {
+ code: 'type T = (0 | (0 | 1)) | number;',
+ errors: [
+ {
+ column: 11,
+ data: {
+ literal: '0 | 0 | 1',
+ primitive: 'number',
+ },
+ messageId: 'literalOverridden',
+ },
+ ],
+ },
+ {
+ code: "type T = (2 | 'other' | 3) | number;",
+ errors: [
+ {
+ column: 11,
+ data: {
+ literal: '2 | 3',
+ primitive: 'number',
+ },
+ messageId: 'literalOverridden',
+ },
+ ],
+ },
+ {
+ code: "type T = '' | string;",
+ errors: [
+ {
+ column: 10,
+ data: {
+ literal: '""',
+ primitive: 'string',
+ },
+ messageId: 'literalOverridden',
+ },
+ ],
+ },
+ {
+ code: `
+ type B = 'b';
+ type T = B | string;
+ `,
+ errors: [
+ {
+ column: 18,
+ data: {
+ literal: '"b"',
+ primitive: 'string',
+ },
+ messageId: 'literalOverridden',
+ },
+ ],
+ },
+ {
+ code: 'type T = `a${number}c` | string;',
+ errors: [
+ {
+ column: 10,
+ data: {
+ literal: 'template literal type',
+ primitive: 'string',
+ },
+ messageId: 'literalOverridden',
+ },
+ ],
+ },
+ {
+ code: `
+ type B = \`a\${number}c\`;
+ type T = B | string;
+ `,
+ errors: [
+ {
+ column: 18,
+ data: {
+ literal: 'template literal type',
+ primitive: 'string',
+ },
+ messageId: 'literalOverridden',
+ },
+ ],
+ },
+ {
+ code: 'type T = `${number}` | string;',
+ errors: [
+ {
+ column: 10,
+ data: {
+ literal: 'template literal type',
+ primitive: 'string',
+ },
+ messageId: 'literalOverridden',
+ },
+ ],
+ },
+ {
+ code: 'type T = 0n | bigint;',
+ errors: [
+ {
+ column: 10,
+ data: {
+ literal: '0n',
+ primitive: 'bigint',
+ },
+ messageId: 'literalOverridden',
+ },
+ ],
+ },
+ {
+ code: 'type T = -1n | bigint;',
+ errors: [
+ {
+ column: 10,
+ data: {
+ literal: '-1n',
+ primitive: 'bigint',
+ },
+ messageId: 'literalOverridden',
+ },
+ ],
+ },
+ {
+ code: 'type T = (-1n | 1n) | bigint;',
+ errors: [
+ {
+ column: 11,
+ data: {
+ literal: '-1n | 1n',
+ primitive: 'bigint',
+ },
+ messageId: 'literalOverridden',
+ },
+ ],
+ },
+ {
+ code: `
+ type B = boolean;
+ type T = B | false;
+ `,
+ errors: [
+ {
+ column: 22,
+ data: {
+ literal: 'false',
+ primitive: 'boolean',
+ },
+ messageId: 'literalOverridden',
+ },
+ ],
+ },
+ {
+ code: 'type T = false | boolean;',
+ errors: [
+ {
+ column: 10,
+ data: {
+ literal: 'false',
+ primitive: 'boolean',
+ },
+ messageId: 'literalOverridden',
+ },
+ ],
+ },
+ {
+ code: 'type T = true | boolean;',
+ errors: [
+ {
+ column: 10,
+ data: {
+ literal: 'true',
+ primitive: 'boolean',
+ },
+ messageId: 'literalOverridden',
+ },
+ ],
+ },
+ {
+ code: 'type T = false & boolean;',
+ errors: [
+ {
+ column: 18,
+ data: {
+ literal: 'false',
+ primitive: 'boolean',
+ },
+ messageId: 'primitiveOverridden',
+ },
+ ],
+ },
+ {
+ code: `
+ type B = false;
+ type T = B & boolean;
+ `,
+ errors: [
+ {
+ column: 22,
+ data: {
+ literal: 'false',
+ primitive: 'boolean',
+ },
+ messageId: 'primitiveOverridden',
+ },
+ ],
+ },
+ {
+ code: `
+ type B = true;
+ type T = B & boolean;
+ `,
+ errors: [
+ {
+ column: 22,
+ data: {
+ literal: 'true',
+ primitive: 'boolean',
+ },
+ messageId: 'primitiveOverridden',
+ },
+ ],
+ },
+ {
+ code: 'type T = true & boolean;',
+ errors: [
+ {
+ column: 17,
+ data: {
+ literal: 'true',
+ primitive: 'boolean',
+ },
+ messageId: 'primitiveOverridden',
+ },
+ ],
+ },
+ {
+ code: 'type T = number & any;',
+ errors: [
+ {
+ column: 19,
+ data: {
+ container: 'intersection',
+ typeName: 'any',
+ },
+ messageId: 'overrides',
+ },
+ ],
+ },
+ {
+ code: 'type T = any & number;',
+ errors: [
+ {
+ column: 10,
+ data: {
+ container: 'intersection',
+ typeName: 'any',
+ },
+ messageId: 'overrides',
+ },
+ ],
+ },
+ {
+ code: 'type T = number & never;',
+ errors: [
+ {
+ column: 19,
+ data: {
+ container: 'intersection',
+ typeName: 'never',
+ },
+ messageId: 'overrides',
+ },
+ ],
+ },
+ {
+ code: `
+ type B = never;
+ type T = B & number;
+ `,
+ errors: [
+ {
+ column: 18,
+ data: {
+ container: 'intersection',
+ typeName: 'never',
+ },
+ messageId: 'overrides',
+ },
+ ],
+ },
+ {
+ code: 'type T = never & number;',
+ errors: [
+ {
+ column: 10,
+ data: {
+ container: 'intersection',
+ typeName: 'never',
+ },
+ messageId: 'overrides',
+ },
+ ],
+ },
+ {
+ code: 'type T = number & unknown;',
+ errors: [
+ {
+ column: 19,
+ data: {
+ container: 'intersection',
+ typeName: 'unknown',
+ },
+ messageId: 'overridden',
+ },
+ ],
+ },
+ {
+ code: 'type T = unknown & number;',
+ errors: [
+ {
+ column: 10,
+ data: {
+ container: 'intersection',
+ typeName: 'unknown',
+ },
+ messageId: 'overridden',
+ },
+ ],
+ },
+ {
+ code: 'type T = number & 0;',
+ errors: [
+ {
+ column: 10,
+ data: {
+ literal: '0',
+ primitive: 'number',
+ },
+ messageId: 'primitiveOverridden',
+ },
+ ],
+ },
+ {
+ code: "type T = '' & string;",
+ errors: [
+ {
+ column: 15,
+ data: {
+ literal: '""',
+ primitive: 'string',
+ },
+ messageId: 'primitiveOverridden',
+ },
+ ],
+ },
+ {
+ code: `
+ type B = 0n;
+ type T = B & bigint;
+ `,
+ errors: [
+ {
+ column: 22,
+ data: {
+ literal: '0n',
+ primitive: 'bigint',
+ },
+ messageId: 'primitiveOverridden',
+ },
+ ],
+ },
+ {
+ code: 'type T = 0n & bigint;',
+ errors: [
+ {
+ column: 15,
+ data: {
+ literal: '0n',
+ primitive: 'bigint',
+ },
+ messageId: 'primitiveOverridden',
+ },
+ ],
+ },
+ {
+ code: 'type T = -1n & bigint;',
+ errors: [
+ {
+ column: 16,
+ data: {
+ literal: '-1n',
+ primitive: 'bigint',
+ },
+ messageId: 'primitiveOverridden',
+ },
+ ],
+ },
+ ],
+});
diff --git a/packages/eslint-plugin/tests/rules/no-useless-empty-export.test.ts b/packages/eslint-plugin/tests/rules/no-useless-empty-export.test.ts
new file mode 100644
index 000000000000..ea13395ec9ed
--- /dev/null
+++ b/packages/eslint-plugin/tests/rules/no-useless-empty-export.test.ts
@@ -0,0 +1,125 @@
+/* eslint-disable eslint-comments/no-use */
+// this rule tests the spacing, which prettier will want to fix and break the tests
+/* eslint "@typescript-eslint/internal/plugin-test-formatting": ["error", { formatWithPrettier: false }] */
+/* eslint-enable eslint-comments/no-use */
+import rule from '../../src/rules/no-useless-empty-export';
+import { RuleTester } from '../RuleTester';
+
+const ruleTester = new RuleTester({
+ parserOptions: {
+ ecmaVersion: 2020,
+ sourceType: 'module',
+ },
+ parser: '@typescript-eslint/parser',
+});
+
+const error = {
+ messageId: 'uselessExport',
+} as const;
+
+ruleTester.run('no-useless-empty-export', rule, {
+ valid: [
+ "declare module '_'",
+ "import {} from '_';",
+ "import * as _ from '_';",
+ 'export = {};',
+ 'export = 3;',
+ 'export const _ = {};',
+ `
+ const _ = {};
+ export default _;
+ `,
+ `
+ export * from '_';
+ export = {};
+ `,
+ `
+ export {};
+ `,
+ ],
+ invalid: [
+ {
+ code: `
+export const _ = {};
+export {};
+ `,
+ errors: [error],
+ output: `
+export const _ = {};
+
+ `,
+ },
+ {
+ code: `
+export * from '_';
+export {};
+ `,
+ errors: [error],
+ output: `
+export * from '_';
+
+ `,
+ },
+ {
+ code: `
+export {};
+export * from '_';
+ `,
+ errors: [error],
+ output: `
+
+export * from '_';
+ `,
+ },
+ {
+ code: `
+const _ = {};
+export default _;
+export {};
+ `,
+ errors: [error],
+ output: `
+const _ = {};
+export default _;
+
+ `,
+ },
+ {
+ code: `
+export {};
+const _ = {};
+export default _;
+ `,
+ errors: [error],
+ output: `
+
+const _ = {};
+export default _;
+ `,
+ },
+ {
+ code: `
+const _ = {};
+export { _ };
+export {};
+ `,
+ errors: [error],
+ output: `
+const _ = {};
+export { _ };
+
+ `,
+ },
+ {
+ code: `
+import _ = require('_');
+export {};
+ `,
+ errors: [error],
+ output: `
+import _ = require('_');
+
+ `,
+ },
+ ],
+});
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 a5eeb2f2fa48..1d29a45ce610 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
@@ -272,6 +272,15 @@ type T =
},
],
},
+ {
+ code: `type Expected = (new (x: number) => boolean) ${operator} string;`,
+ output: `type Expected = string ${operator} (new (x: number) => boolean);`,
+ errors: [
+ {
+ messageId: 'notSortedNamed',
+ },
+ ],
+ },
];
};
diff --git a/packages/eslint-plugin/tests/rules/space-before-blocks.test.ts b/packages/eslint-plugin/tests/rules/space-before-blocks.test.ts
new file mode 100644
index 000000000000..7e1338c6fc5d
--- /dev/null
+++ b/packages/eslint-plugin/tests/rules/space-before-blocks.test.ts
@@ -0,0 +1,258 @@
+/* eslint-disable eslint-comments/no-use */
+// this rule tests spacing, which prettier will want to fix and break the tests
+/* eslint "@typescript-eslint/internal/plugin-test-formatting": ["error", { formatWithPrettier: false }] */
+/* eslint-enable eslint-comments/no-use */
+
+import rule from '../../src/rules/space-before-blocks';
+import { RuleTester } from '../RuleTester';
+
+const ruleTester = new RuleTester({
+ parser: '@typescript-eslint/parser',
+});
+
+ruleTester.run('space-before-blocks', rule, {
+ valid: [
+ {
+ code: `
+ enum Test{
+ KEY1 = 2,
+ }
+ `,
+ options: ['never'],
+ },
+ {
+ code: `
+ interface Test{
+ prop1: number;
+ }
+ `,
+ options: ['never'],
+ },
+ {
+ code: `
+ enum Test {
+ KEY1 = 2,
+ }
+ `,
+ options: ['always'],
+ },
+ {
+ code: `
+ interface Test {
+ prop1: number;
+ }
+ `,
+ options: ['always'],
+ },
+ {
+ code: `
+ enum Test{
+ KEY1 = 2,
+ }
+ `,
+ options: [{ classes: 'never' }],
+ },
+ {
+ code: `
+ interface Test{
+ prop1: number;
+ }
+ `,
+ options: [{ classes: 'never' }],
+ },
+ {
+ code: `
+ enum Test {
+ KEY1 = 2,
+ }
+ `,
+ options: [{ classes: 'always' }],
+ },
+ {
+ code: `
+ interface Test {
+ prop1: number;
+ }
+ `,
+ options: [{ classes: 'always' }],
+ },
+ {
+ code: `
+ interface Test{
+ prop1: number;
+ }
+ `,
+ options: [{ classes: 'off' }],
+ },
+ ],
+ invalid: [
+ {
+ code: `
+ enum Test{
+ A = 2,
+ B = 1,
+ }
+ `,
+ output: `
+ enum Test {
+ A = 2,
+ B = 1,
+ }
+ `,
+ errors: [
+ {
+ messageId: 'missingSpace',
+ column: 18,
+ line: 2,
+ },
+ ],
+ options: ['always'],
+ },
+ {
+ code: `
+ interface Test{
+ prop1: number;
+ }
+ `,
+ output: `
+ interface Test {
+ prop1: number;
+ }
+ `,
+ errors: [
+ {
+ messageId: 'missingSpace',
+ column: 23,
+ line: 2,
+ },
+ ],
+ options: ['always'],
+ },
+ {
+ code: `
+ enum Test{
+ A = 2,
+ B = 1,
+ }
+ `,
+ output: `
+ enum Test {
+ A = 2,
+ B = 1,
+ }
+ `,
+ errors: [
+ {
+ messageId: 'missingSpace',
+ column: 18,
+ line: 2,
+ },
+ ],
+ options: [{ classes: 'always' }],
+ },
+ {
+ code: `
+ interface Test{
+ prop1: number;
+ }
+ `,
+ output: `
+ interface Test {
+ prop1: number;
+ }
+ `,
+ errors: [
+ {
+ messageId: 'missingSpace',
+ column: 23,
+ line: 2,
+ },
+ ],
+ options: [{ classes: 'always' }],
+ },
+ {
+ code: `
+ enum Test {
+ A = 2,
+ B = 1,
+ }
+ `,
+ output: `
+ enum Test{
+ A = 2,
+ B = 1,
+ }
+ `,
+ errors: [
+ {
+ messageId: 'unexpectedSpace',
+ column: 19,
+ line: 2,
+ },
+ ],
+ options: ['never'],
+ },
+ {
+ code: `
+ interface Test {
+ prop1: number;
+ }
+ `,
+ output: `
+ interface Test{
+ prop1: number;
+ }
+ `,
+ errors: [
+ {
+ messageId: 'unexpectedSpace',
+ column: 24,
+ line: 2,
+ },
+ ],
+ options: ['never'],
+ },
+ {
+ code: `
+ enum Test {
+ A = 2,
+ B = 1,
+ }
+ `,
+ output: `
+ enum Test{
+ A = 2,
+ B = 1,
+ }
+ `,
+ errors: [
+ {
+ messageId: 'unexpectedSpace',
+ column: 19,
+ line: 2,
+ },
+ ],
+ options: [{ classes: 'never' }],
+ },
+ {
+ code: `
+ interface Test {
+ prop1: number;
+ }
+ `,
+ output: `
+ interface Test{
+ prop1: number;
+ }
+ `,
+ errors: [
+ {
+ messageId: 'unexpectedSpace',
+ column: 24,
+ line: 2,
+ },
+ ],
+ options: [{ classes: 'never' }],
+ },
+ ],
+});
diff --git a/packages/eslint-plugin/typings/eslint-rules.d.ts b/packages/eslint-plugin/typings/eslint-rules.d.ts
index d53ef43a747f..fa7d5934b263 100644
--- a/packages/eslint-plugin/typings/eslint-rules.d.ts
+++ b/packages/eslint-plugin/typings/eslint-rules.d.ts
@@ -842,6 +842,29 @@ declare module 'eslint/lib/rules/space-infix-ops' {
export = rule;
}
+declare module 'eslint/lib/rules/space-before-blocks' {
+ import { TSESLint, TSESTree } from '@typescript-eslint/utils';
+
+ const rule: TSESLint.RuleModule<
+ 'missingSpace' | 'unexpectedSpace',
+ [
+ | 'always'
+ | 'never'
+ | {
+ classes?: 'always' | 'never' | 'off';
+ functions?: 'always' | 'never' | 'off';
+ keywords?: 'always' | 'never' | 'off';
+ },
+ ],
+ {
+ BlockStatement(node: TSESTree.BlockStatement): void;
+ ClassBody(node: TSESTree.ClassBody): void;
+ SwitchStatement(node: TSESTree.SwitchStatement): void;
+ }
+ >;
+ export = rule;
+}
+
declare module 'eslint/lib/rules/prefer-const' {
import { TSESLint, TSESTree } from '@typescript-eslint/utils';
diff --git a/packages/experimental-utils/CHANGELOG.md b/packages/experimental-utils/CHANGELOG.md
index 6a2cbe78f1ac..a2431353a456 100644
--- a/packages/experimental-utils/CHANGELOG.md
+++ b/packages/experimental-utils/CHANGELOG.md
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [5.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.1...v5.13.0) (2022-02-28)
+
+**Note:** Version bump only for package @typescript-eslint/experimental-utils
+
+
+
+
+
## [5.12.1](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.0...v5.12.1) (2022-02-21)
**Note:** Version bump only for package @typescript-eslint/experimental-utils
diff --git a/packages/experimental-utils/package.json b/packages/experimental-utils/package.json
index 1bc0685e05a8..2bf8d3f2cfbc 100644
--- a/packages/experimental-utils/package.json
+++ b/packages/experimental-utils/package.json
@@ -1,6 +1,6 @@
{
"name": "@typescript-eslint/experimental-utils",
- "version": "5.12.1",
+ "version": "5.13.0",
"description": "(Experimental) Utilities for working with TypeScript + ESLint together",
"keywords": [
"eslint",
@@ -38,7 +38,7 @@
"typecheck": "tsc -p tsconfig.json --noEmit"
},
"dependencies": {
- "@typescript-eslint/utils": "5.12.1"
+ "@typescript-eslint/utils": "5.13.0"
},
"peerDependencies": {
"eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
diff --git a/packages/parser/CHANGELOG.md b/packages/parser/CHANGELOG.md
index 1f0b280b4506..eb904406d62b 100644
--- a/packages/parser/CHANGELOG.md
+++ b/packages/parser/CHANGELOG.md
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [5.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.1...v5.13.0) (2022-02-28)
+
+**Note:** Version bump only for package @typescript-eslint/parser
+
+
+
+
+
## [5.12.1](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.0...v5.12.1) (2022-02-21)
**Note:** Version bump only for package @typescript-eslint/parser
diff --git a/packages/parser/package.json b/packages/parser/package.json
index c9f6f962a510..bbe3659fc921 100644
--- a/packages/parser/package.json
+++ b/packages/parser/package.json
@@ -1,6 +1,6 @@
{
"name": "@typescript-eslint/parser",
- "version": "5.12.1",
+ "version": "5.13.0",
"description": "An ESLint custom parser which leverages TypeScript ESTree",
"main": "dist/index.js",
"types": "dist/index.d.ts",
@@ -44,9 +44,9 @@
"eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
},
"dependencies": {
- "@typescript-eslint/scope-manager": "5.12.1",
- "@typescript-eslint/types": "5.12.1",
- "@typescript-eslint/typescript-estree": "5.12.1",
+ "@typescript-eslint/scope-manager": "5.13.0",
+ "@typescript-eslint/types": "5.13.0",
+ "@typescript-eslint/typescript-estree": "5.13.0",
"debug": "^4.3.2"
},
"devDependencies": {
diff --git a/packages/scope-manager/CHANGELOG.md b/packages/scope-manager/CHANGELOG.md
index e0dd15ec5814..faa953a077a6 100644
--- a/packages/scope-manager/CHANGELOG.md
+++ b/packages/scope-manager/CHANGELOG.md
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [5.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.1...v5.13.0) (2022-02-28)
+
+**Note:** Version bump only for package @typescript-eslint/scope-manager
+
+
+
+
+
## [5.12.1](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.0...v5.12.1) (2022-02-21)
**Note:** Version bump only for package @typescript-eslint/scope-manager
diff --git a/packages/scope-manager/package.json b/packages/scope-manager/package.json
index b4ddedb62332..c010b60160d7 100644
--- a/packages/scope-manager/package.json
+++ b/packages/scope-manager/package.json
@@ -1,6 +1,6 @@
{
"name": "@typescript-eslint/scope-manager",
- "version": "5.12.1",
+ "version": "5.13.0",
"description": "TypeScript scope analyser for ESLint",
"keywords": [
"eslint",
@@ -39,12 +39,12 @@
"typecheck": "tsc -p tsconfig.json --noEmit"
},
"dependencies": {
- "@typescript-eslint/types": "5.12.1",
- "@typescript-eslint/visitor-keys": "5.12.1"
+ "@typescript-eslint/types": "5.13.0",
+ "@typescript-eslint/visitor-keys": "5.13.0"
},
"devDependencies": {
"@types/glob": "*",
- "@typescript-eslint/typescript-estree": "5.12.1",
+ "@typescript-eslint/typescript-estree": "5.13.0",
"glob": "*",
"jest-specific-snapshot": "*",
"make-dir": "*",
diff --git a/packages/shared-fixtures/CHANGELOG.md b/packages/shared-fixtures/CHANGELOG.md
index f935e72e5ba3..66d60cd5e848 100644
--- a/packages/shared-fixtures/CHANGELOG.md
+++ b/packages/shared-fixtures/CHANGELOG.md
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [5.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.1...v5.13.0) (2022-02-28)
+
+**Note:** Version bump only for package @typescript-eslint/shared-fixtures
+
+
+
+
+
## [5.12.1](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.0...v5.12.1) (2022-02-21)
**Note:** Version bump only for package @typescript-eslint/shared-fixtures
diff --git a/packages/shared-fixtures/package.json b/packages/shared-fixtures/package.json
index 762ad6131868..b350512e48bb 100644
--- a/packages/shared-fixtures/package.json
+++ b/packages/shared-fixtures/package.json
@@ -1,5 +1,5 @@
{
"name": "@typescript-eslint/shared-fixtures",
- "version": "5.12.1",
+ "version": "5.13.0",
"private": true
}
diff --git a/packages/type-utils/CHANGELOG.md b/packages/type-utils/CHANGELOG.md
index 56e63599e4cd..7fee288a635f 100644
--- a/packages/type-utils/CHANGELOG.md
+++ b/packages/type-utils/CHANGELOG.md
@@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [5.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.1...v5.13.0) (2022-02-28)
+
+
+### Features
+
+* **eslint-plugin:** add `no-redundant-type-constituents` rule ([#4378](https://github.com/typescript-eslint/typescript-eslint/issues/4378)) ([63d051e](https://github.com/typescript-eslint/typescript-eslint/commit/63d051eed29dcf71015a23992feac0a8f92717a0))
+
+
+
+
+
## [5.12.1](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.0...v5.12.1) (2022-02-21)
diff --git a/packages/type-utils/package.json b/packages/type-utils/package.json
index 1f5685e67955..16a23faf7702 100644
--- a/packages/type-utils/package.json
+++ b/packages/type-utils/package.json
@@ -1,6 +1,6 @@
{
"name": "@typescript-eslint/type-utils",
- "version": "5.12.1",
+ "version": "5.13.0",
"description": "Type utilities for working with TypeScript + ESLint together",
"keywords": [
"eslint",
@@ -39,12 +39,12 @@
"typecheck": "tsc -p tsconfig.json --noEmit"
},
"dependencies": {
- "@typescript-eslint/utils": "5.12.1",
+ "@typescript-eslint/utils": "5.13.0",
"debug": "^4.3.2",
"tsutils": "^3.21.0"
},
"devDependencies": {
- "@typescript-eslint/parser": "5.12.1",
+ "@typescript-eslint/parser": "5.13.0",
"typescript": "*"
},
"peerDependencies": {
diff --git a/packages/type-utils/src/predicates.ts b/packages/type-utils/src/predicates.ts
index c3ece8aa3c7a..541d236bd7c9 100644
--- a/packages/type-utils/src/predicates.ts
+++ b/packages/type-utils/src/predicates.ts
@@ -47,6 +47,13 @@ export function isTypeArrayTypeOrUnionOfArrayTypes(
return true;
}
+/**
+ * @returns true if the type is `never`
+ */
+export function isTypeNeverType(type: ts.Type): boolean {
+ return isTypeFlagSet(type, ts.TypeFlags.Never);
+}
+
/**
* @returns true if the type is `unknown`
*/
@@ -168,3 +175,15 @@ export function typeIsOrHasBaseType(
return false;
}
+
+export function isTypeBigIntLiteralType(
+ type: ts.Type,
+): type is ts.BigIntLiteralType {
+ return isTypeFlagSet(type, ts.TypeFlags.BigIntLiteral);
+}
+
+export function isTypeTemplateLiteralType(
+ type: ts.Type,
+): type is ts.TemplateLiteralType {
+ return isTypeFlagSet(type, ts.TypeFlags.TemplateLiteral);
+}
diff --git a/packages/types/CHANGELOG.md b/packages/types/CHANGELOG.md
index 90dfc70926e4..26062862c481 100644
--- a/packages/types/CHANGELOG.md
+++ b/packages/types/CHANGELOG.md
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [5.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.1...v5.13.0) (2022-02-28)
+
+**Note:** Version bump only for package @typescript-eslint/types
+
+
+
+
+
## [5.12.1](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.0...v5.12.1) (2022-02-21)
**Note:** Version bump only for package @typescript-eslint/types
diff --git a/packages/types/package.json b/packages/types/package.json
index 206ebf9545ed..a0193e6067aa 100644
--- a/packages/types/package.json
+++ b/packages/types/package.json
@@ -1,6 +1,6 @@
{
"name": "@typescript-eslint/types",
- "version": "5.12.1",
+ "version": "5.13.0",
"description": "Types for the TypeScript-ESTree AST spec",
"keywords": [
"eslint",
diff --git a/packages/typescript-estree/CHANGELOG.md b/packages/typescript-estree/CHANGELOG.md
index 4d7ad425b84b..6919033b0411 100644
--- a/packages/typescript-estree/CHANGELOG.md
+++ b/packages/typescript-estree/CHANGELOG.md
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [5.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.1...v5.13.0) (2022-02-28)
+
+**Note:** Version bump only for package @typescript-eslint/typescript-estree
+
+
+
+
+
## [5.12.1](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.0...v5.12.1) (2022-02-21)
**Note:** Version bump only for package @typescript-eslint/typescript-estree
diff --git a/packages/typescript-estree/package.json b/packages/typescript-estree/package.json
index fa26a4ef6892..d4ec037e5df9 100644
--- a/packages/typescript-estree/package.json
+++ b/packages/typescript-estree/package.json
@@ -1,6 +1,6 @@
{
"name": "@typescript-eslint/typescript-estree",
- "version": "5.12.1",
+ "version": "5.13.0",
"description": "A parser that converts TypeScript source code into an ESTree compatible form",
"main": "dist/index.js",
"types": "dist/index.d.ts",
@@ -41,8 +41,8 @@
"typecheck": "tsc -p tsconfig.json --noEmit"
},
"dependencies": {
- "@typescript-eslint/types": "5.12.1",
- "@typescript-eslint/visitor-keys": "5.12.1",
+ "@typescript-eslint/types": "5.13.0",
+ "@typescript-eslint/visitor-keys": "5.13.0",
"debug": "^4.3.2",
"globby": "^11.0.4",
"is-glob": "^4.0.3",
@@ -59,7 +59,7 @@
"@types/is-glob": "*",
"@types/semver": "*",
"@types/tmp": "*",
- "@typescript-eslint/shared-fixtures": "5.12.1",
+ "@typescript-eslint/shared-fixtures": "5.13.0",
"glob": "*",
"jest-specific-snapshot": "*",
"make-dir": "*",
diff --git a/packages/utils/CHANGELOG.md b/packages/utils/CHANGELOG.md
index f1c92f415df4..a699c9f1aad6 100644
--- a/packages/utils/CHANGELOG.md
+++ b/packages/utils/CHANGELOG.md
@@ -3,6 +3,19 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [5.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.1...v5.13.0) (2022-02-28)
+
+
+### Features
+
+* **eslint-plugin:** add extension rule `space-before-blocks` ([#1606](https://github.com/typescript-eslint/typescript-eslint/issues/1606)) ([#4184](https://github.com/typescript-eslint/typescript-eslint/issues/4184)) ([208b6d0](https://github.com/typescript-eslint/typescript-eslint/commit/208b6d02252dff2bf272329d3e4a4a82e56c52c0))
+* **utils:** expose `ast-utils`' helpers ([#4503](https://github.com/typescript-eslint/typescript-eslint/issues/4503)) ([f106e4b](https://github.com/typescript-eslint/typescript-eslint/commit/f106e4b95e824ebb68141bce3d3207448d50c860))
+* **utils:** extract `isNotTokenOfTypeWithConditions` out of `ast-utils`' `predicates` ([#4502](https://github.com/typescript-eslint/typescript-eslint/issues/4502)) ([66501d6](https://github.com/typescript-eslint/typescript-eslint/commit/66501d6dd7e97c22c671efaa6d1ba8237907e417))
+
+
+
+
+
## [5.12.1](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.0...v5.12.1) (2022-02-21)
**Note:** Version bump only for package @typescript-eslint/utils
diff --git a/packages/utils/package.json b/packages/utils/package.json
index d3fafa8278fd..03d6e47493e1 100644
--- a/packages/utils/package.json
+++ b/packages/utils/package.json
@@ -1,6 +1,6 @@
{
"name": "@typescript-eslint/utils",
- "version": "5.12.1",
+ "version": "5.13.0",
"description": "Utilities for working with TypeScript + ESLint together",
"keywords": [
"eslint",
@@ -40,9 +40,9 @@
},
"dependencies": {
"@types/json-schema": "^7.0.9",
- "@typescript-eslint/scope-manager": "5.12.1",
- "@typescript-eslint/types": "5.12.1",
- "@typescript-eslint/typescript-estree": "5.12.1",
+ "@typescript-eslint/scope-manager": "5.13.0",
+ "@typescript-eslint/types": "5.13.0",
+ "@typescript-eslint/typescript-estree": "5.13.0",
"eslint-scope": "^5.1.1",
"eslint-utils": "^3.0.0"
},
diff --git a/packages/utils/src/ast-utils/helpers.ts b/packages/utils/src/ast-utils/helpers.ts
index f2a4ac2e2442..baf4daf68101 100644
--- a/packages/utils/src/ast-utils/helpers.ts
+++ b/packages/utils/src/ast-utils/helpers.ts
@@ -56,3 +56,24 @@ export const isTokenOfTypeWithConditions = <
token?.type === tokenType &&
entries.every(([key, value]) => token[key] === value);
};
+
+export const isNotTokenOfTypeWithConditions =
+ <
+ TokenType extends AST_TOKEN_TYPES,
+ Conditions extends Partial,
+ >(
+ tokenType: TokenType,
+ conditions: Conditions,
+ ): ((
+ token: TSESTree.Token | null | undefined,
+ ) => token is Exclude<
+ TSESTree.Token,
+ TSESTree.Token & { type: TokenType } & Conditions
+ >) =>
+ (
+ token,
+ ): token is Exclude<
+ TSESTree.Token,
+ TSESTree.Token & { type: TokenType } & Conditions
+ > =>
+ !isTokenOfTypeWithConditions(tokenType, conditions)(token);
diff --git a/packages/utils/src/ast-utils/index.ts b/packages/utils/src/ast-utils/index.ts
index c0ab325299ad..66e528aa3e34 100644
--- a/packages/utils/src/ast-utils/index.ts
+++ b/packages/utils/src/ast-utils/index.ts
@@ -1,3 +1,4 @@
+export * from './eslint-utils';
+export * from './helpers';
export * from './misc';
export * from './predicates';
-export * from './eslint-utils';
diff --git a/packages/utils/src/ast-utils/misc.ts b/packages/utils/src/ast-utils/misc.ts
index 923424d9f928..72a7898c31c0 100644
--- a/packages/utils/src/ast-utils/misc.ts
+++ b/packages/utils/src/ast-utils/misc.ts
@@ -6,8 +6,8 @@ const LINEBREAK_MATCHER = /\r\n|[\r\n\u2028\u2029]/;
* Determines whether two adjacent tokens are on the same line
*/
function isTokenOnSameLine(
- left: TSESTree.Token,
- right: TSESTree.Token,
+ left: TSESTree.Node | TSESTree.Token,
+ right: TSESTree.Node | TSESTree.Token,
): boolean {
return left.loc.end.line === right.loc.start.line;
}
diff --git a/packages/utils/src/ast-utils/predicates.ts b/packages/utils/src/ast-utils/predicates.ts
index 0038f092bbe0..10e50522d672 100644
--- a/packages/utils/src/ast-utils/predicates.ts
+++ b/packages/utils/src/ast-utils/predicates.ts
@@ -4,6 +4,7 @@ import {
isNodeOfType,
isNodeOfTypes,
isNodeOfTypeWithConditions,
+ isNotTokenOfTypeWithConditions,
isTokenOfTypeWithConditions,
} from './helpers';
@@ -12,25 +13,20 @@ const isOptionalChainPunctuator = isTokenOfTypeWithConditions(
{ value: '?.' },
);
-function isNotOptionalChainPunctuator(
- token: TSESTree.Token,
-): token is Exclude<
- TSESTree.Token,
- TSESTree.PunctuatorToken & { value: '?.' }
-> {
- return !isOptionalChainPunctuator(token);
-}
+const isNotOptionalChainPunctuator = isNotTokenOfTypeWithConditions(
+ AST_TOKEN_TYPES.Punctuator,
+ { value: '?.' },
+);
const isNonNullAssertionPunctuator = isTokenOfTypeWithConditions(
AST_TOKEN_TYPES.Punctuator,
{ value: '!' },
);
-function isNotNonNullAssertionPunctuator(
- token: TSESTree.Token,
-): token is Exclude {
- return !isNonNullAssertionPunctuator(token);
-}
+const isNotNonNullAssertionPunctuator = isNotTokenOfTypeWithConditions(
+ AST_TOKEN_TYPES.Punctuator,
+ { value: '!' },
+);
/**
* Returns true if and only if the node represents: foo?.() or foo.bar?.()
diff --git a/packages/visitor-keys/CHANGELOG.md b/packages/visitor-keys/CHANGELOG.md
index 88312dcfe057..5e0ff65320fc 100644
--- a/packages/visitor-keys/CHANGELOG.md
+++ b/packages/visitor-keys/CHANGELOG.md
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [5.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.1...v5.13.0) (2022-02-28)
+
+**Note:** Version bump only for package @typescript-eslint/visitor-keys
+
+
+
+
+
## [5.12.1](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.0...v5.12.1) (2022-02-21)
**Note:** Version bump only for package @typescript-eslint/visitor-keys
diff --git a/packages/visitor-keys/package.json b/packages/visitor-keys/package.json
index 0fc64f259de9..798598914661 100644
--- a/packages/visitor-keys/package.json
+++ b/packages/visitor-keys/package.json
@@ -1,6 +1,6 @@
{
"name": "@typescript-eslint/visitor-keys",
- "version": "5.12.1",
+ "version": "5.13.0",
"description": "Visitor keys used to help traverse the TypeScript-ESTree AST",
"keywords": [
"eslint",
@@ -38,7 +38,7 @@
"typecheck": "tsc -p tsconfig.json --noEmit"
},
"dependencies": {
- "@typescript-eslint/types": "5.12.1",
+ "@typescript-eslint/types": "5.13.0",
"eslint-visitor-keys": "^3.0.0"
},
"devDependencies": {
diff --git a/packages/website-eslint/CHANGELOG.md b/packages/website-eslint/CHANGELOG.md
index 6268851171ce..928b86d944d3 100644
--- a/packages/website-eslint/CHANGELOG.md
+++ b/packages/website-eslint/CHANGELOG.md
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [5.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.1...v5.13.0) (2022-02-28)
+
+**Note:** Version bump only for package @typescript-eslint/website-eslint
+
+
+
+
+
## [5.12.1](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.0...v5.12.1) (2022-02-21)
**Note:** Version bump only for package @typescript-eslint/website-eslint
diff --git a/packages/website-eslint/package.json b/packages/website-eslint/package.json
index 45ec14a00ffe..5d1eb3dfa376 100644
--- a/packages/website-eslint/package.json
+++ b/packages/website-eslint/package.json
@@ -1,6 +1,6 @@
{
"name": "@typescript-eslint/website-eslint",
- "version": "5.12.1",
+ "version": "5.13.0",
"private": true,
"description": "ESLint which works in browsers.",
"engines": {
@@ -16,19 +16,19 @@
"format": "prettier --write \"./**/*.{ts,js,json,md}\" --ignore-path ../../.prettierignore"
},
"dependencies": {
- "@typescript-eslint/types": "5.12.1",
- "@typescript-eslint/utils": "5.12.1"
+ "@typescript-eslint/types": "5.13.0",
+ "@typescript-eslint/utils": "5.13.0"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^21.0.1",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^13.0.6",
"@rollup/pluginutils": "^4.1.1",
- "@typescript-eslint/eslint-plugin": "5.12.1",
- "@typescript-eslint/parser": "5.12.1",
- "@typescript-eslint/scope-manager": "5.12.1",
- "@typescript-eslint/typescript-estree": "5.12.1",
- "@typescript-eslint/visitor-keys": "5.12.1",
+ "@typescript-eslint/eslint-plugin": "5.13.0",
+ "@typescript-eslint/parser": "5.13.0",
+ "@typescript-eslint/scope-manager": "5.13.0",
+ "@typescript-eslint/typescript-estree": "5.13.0",
+ "@typescript-eslint/visitor-keys": "5.13.0",
"eslint": "*",
"rollup": "^2.59.0",
"semver": "^7.3.5"
diff --git a/packages/website/CHANGELOG.md b/packages/website/CHANGELOG.md
index d29faabc6c4d..85539eb69c32 100644
--- a/packages/website/CHANGELOG.md
+++ b/packages/website/CHANGELOG.md
@@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [5.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.1...v5.13.0) (2022-02-28)
+
+
+### Features
+
+* **eslint-plugin:** [no-misused-promises] check more places for checksVoidReturn ([#4541](https://github.com/typescript-eslint/typescript-eslint/issues/4541)) ([052cf51](https://github.com/typescript-eslint/typescript-eslint/commit/052cf51fe663283afe89dc7bf97c947e750df095))
+
+
+
+
+
## [5.12.1](https://github.com/typescript-eslint/typescript-eslint/compare/v5.12.0...v5.12.1) (2022-02-21)
**Note:** Version bump only for package website
diff --git a/packages/website/package.json b/packages/website/package.json
index 50f187c41011..36f082444338 100644
--- a/packages/website/package.json
+++ b/packages/website/package.json
@@ -1,6 +1,6 @@
{
"name": "website",
- "version": "5.12.1",
+ "version": "5.13.0",
"private": true,
"scripts": {
"build": "docusaurus build",
@@ -22,7 +22,7 @@
"@docusaurus/theme-common": "^2.0.0-beta.15",
"@docusaurus/theme-search-algolia": "^2.0.0-beta.15",
"@mdx-js/react": "1.6.22",
- "@typescript-eslint/website-eslint": "5.12.1",
+ "@typescript-eslint/website-eslint": "5.13.0",
"clsx": "^1.1.1",
"eslint": "*",
"json5": "^2.2.0",
diff --git a/packages/website/src/components/OptionsSelector.tsx b/packages/website/src/components/OptionsSelector.tsx
index 83715fc3ae5c..86c842965073 100644
--- a/packages/website/src/components/OptionsSelector.tsx
+++ b/packages/website/src/components/OptionsSelector.tsx
@@ -74,17 +74,21 @@ function OptionsSelector({
[setState],
);
- const copyLinkToClipboard = useCallback(async () => {
- await navigator.clipboard.writeText(document.location.toString());
- setCopyLink(true);
+ const copyLinkToClipboard = useCallback(() => {
+ void navigator.clipboard
+ .writeText(document.location.toString())
+ .then(() => {
+ setCopyLink(true);
+ });
}, []);
- const copyMarkdownToClipboard = useCallback(async () => {
+ const copyMarkdownToClipboard = useCallback(() => {
if (isLoading) {
return;
}
- await navigator.clipboard.writeText(createMarkdown(state));
- setCopyMarkdown(true);
+ void navigator.clipboard.writeText(createMarkdown(state)).then(() => {
+ setCopyMarkdown(true);
+ });
}, [state, isLoading]);
const openIssue = useCallback(() => {
diff --git a/yarn.lock b/yarn.lock
index e86a9f956597..ccca33603c21 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -6887,7 +6887,7 @@ end-of-stream@^1.1.0:
dependencies:
once "^1.4.0"
-enhanced-resolve@^5.8.3, enhanced-resolve@^5.9.0:
+enhanced-resolve@^5.8.3:
version "5.9.0"
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.9.0.tgz#49ac24953ac8452ed8fed2ef1340fc8e043667ee"
integrity sha512-weDYmzbBygL7HzGGS26M3hGQx68vehdEg6VUmqSOaFzXExFqlnKuSvsEJCVGQHScS8CQMbrAqftT+AzzHNt/YA==
@@ -13468,9 +13468,9 @@ rimraf@^2.6.3:
glob "^7.1.3"
rollup@^2.59.0:
- version "2.67.2"
- resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.67.2.tgz#d95e15f60932ad21e05a870bd0aa0b235d056f04"
- integrity sha512-hoEiBWwZtf1QdK3jZIq59L0FJj4Fiv4RplCO4pvCRC86qsoFurWB4hKQIjoRf3WvJmk5UZ9b0y5ton+62fC7Tw==
+ version "2.67.3"
+ resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.67.3.tgz#3f04391fc296f807d067c9081d173e0a33dbd37e"
+ integrity sha512-G/x1vUwbGtP6O5ZM8/sWr8+p7YfZhI18pPqMRtMYMWSbHjKZ/ajHGiM+GWNTlWyOR0EHIdT8LHU+Z4ciIZ1oBw==
optionalDependencies:
fsevents "~2.3.2"
@@ -15373,9 +15373,9 @@ webpack-sources@^3.2.3:
integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==
webpack@^5.61.0, webpack@^5.64.0:
- version "5.69.0"
- resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.69.0.tgz#c9eb607d4f6c49f1e5755492323a7b055c3450e3"
- integrity sha512-E5Fqu89Gu8fR6vejRqu26h8ld/k6/dCVbeGUcuZjc+goQHDfCPU9rER71JmdtBYGmci7Ec2aFEATQ2IVXKy2wg==
+ version "5.69.1"
+ resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.69.1.tgz#8cfd92c192c6a52c99ab00529b5a0d33aa848dc5"
+ integrity sha512-+VyvOSJXZMT2V5vLzOnDuMz5GxEqLk7hKWQ56YxPW/PQRUuKimPqmEIJOx8jHYeyo65pKbapbW464mvsKbaj4A==
dependencies:
"@types/eslint-scope" "^3.7.3"
"@types/estree" "^0.0.51"
@@ -15386,7 +15386,7 @@ webpack@^5.61.0, webpack@^5.64.0:
acorn-import-assertions "^1.7.6"
browserslist "^4.14.5"
chrome-trace-event "^1.0.2"
- enhanced-resolve "^5.9.0"
+ enhanced-resolve "^5.8.3"
es-module-lexer "^0.9.0"
eslint-scope "5.1.1"
events "^3.2.0"