diff --git a/docs/maintenance/Issues.mdx b/docs/maintenance/Issues.mdx
index 525147f88c54..8b39f5e5506d 100644
--- a/docs/maintenance/Issues.mdx
+++ b/docs/maintenance/Issues.mdx
@@ -163,7 +163,7 @@ For enhancements meant to limit which kinds of nodes the rule targets, mark the
#### 🚀 New Rules
We're generally accepting of new rules that meet the above feature request criteria.
-The biggest exception is rules that can roughly be implemented with [`@typescript-eslint/ban-types`](/rules/ban-types) and/or [`no-restricted-syntax`](https://eslint.org/docs/latest/rules/no-restricted-syntax).
+The biggest exception is rules that can roughly be implemented with [`@typescript-eslint/no-restricted-types`](/rules/no-restricted-types) and/or [`no-restricted-syntax`](https://eslint.org/docs/latest/rules/no-restricted-syntax).
## Pruning Old Issues
diff --git a/packages/eslint-plugin/TSLINT_RULE_ALTERNATIVES.md b/packages/eslint-plugin/TSLINT_RULE_ALTERNATIVES.md
index b462a35c0511..b0fc34b3fc5f 100644
--- a/packages/eslint-plugin/TSLINT_RULE_ALTERNATIVES.md
+++ b/packages/eslint-plugin/TSLINT_RULE_ALTERNATIVES.md
@@ -15,32 +15,32 @@ It lists all TSLint rules along side rules from the ESLint ecosystem that are th
### TypeScript-specific
-| TSLint rule | | ESLint rule |
-| --------------------------------- | :-: | ---------------------------------------------------- |
-| [`adjacent-overload-signatures`] | ✅ | [`@typescript-eslint/adjacent-overload-signatures`] |
-| [`ban-ts-ignore`] | ✅ | [`@typescript-eslint/ban-ts-comment`] |
-| [`ban-types`] | 🌓 | [`@typescript-eslint/ban-types`][1] |
-| [`invalid-void`] | ✅ | [`@typescript-eslint/no-invalid-void-type`] |
-| [`member-access`] | ✅ | [`@typescript-eslint/explicit-member-accessibility`] |
-| [`member-ordering`] | ✅ | [`@typescript-eslint/member-ordering`] |
-| [`no-any`] | ✅ | [`@typescript-eslint/no-explicit-any`] |
-| [`no-empty-interface`] | ✅ | [`@typescript-eslint/no-empty-object-type`] |
-| [`no-import-side-effect`] | 🔌 | [`import/no-unassigned-import`] |
-| [`no-inferrable-types`] | ✅ | [`@typescript-eslint/no-inferrable-types`] |
-| [`no-internal-module`] | ✅ | [`@typescript-eslint/prefer-namespace-keyword`] |
-| [`no-magic-numbers`] | ✅ | [`@typescript-eslint/no-magic-numbers`] |
-| [`no-namespace`] | ✅ | [`@typescript-eslint/no-namespace`] |
-| [`no-non-null-assertion`] | ✅ | [`@typescript-eslint/no-non-null-assertion`] |
-| [`no-parameter-reassignment`] | ✅ | [`no-param-reassign`][no-param-reassign] |
-| [`no-reference`] | ✅ | [`@typescript-eslint/triple-slash-reference`] |
-| [`no-unnecessary-type-assertion`] | ✅ | [`@typescript-eslint/no-unnecessary-type-assertion`] |
-| [`no-var-requires`] | ✅ | [`@typescript-eslint/no-var-requires`] |
-| [`only-arrow-functions`] | 🔌 | [`prefer-arrow/prefer-arrow-functions`] |
-| [`prefer-for-of`] | ✅ | [`@typescript-eslint/prefer-for-of`] |
-| [`promise-function-async`] | ✅ | [`@typescript-eslint/promise-function-async`] |
-| [`typedef-whitespace`] | ✅ | [`@typescript-eslint/type-annotation-spacing`] |
-| [`typedef`] | ✅ | [`@typescript-eslint/typedef`] |
-| [`unified-signatures`] | ✅ | [`@typescript-eslint/unified-signatures`] |
+| TSLint rule | | ESLint rule |
+| --------------------------------- | :-: | -------------------------------------------------------- |
+| [`adjacent-overload-signatures`] | ✅ | [`@typescript-eslint/adjacent-overload-signatures`] |
+| [`ban-ts-ignore`] | ✅ | [`@typescript-eslint/ban-ts-comment`] |
+| [`ban-types`] | 🌓 | [`@typescript-eslint/no-restricted-types`][1] |
+| [`invalid-void`] | ✅ | [`@typescript-eslint/no-invalid-void-type`] |
+| [`member-access`] | ✅ | [`@typescript-eslint/explicit-member-accessibility`] |
+| [`member-ordering`] | ✅ | [`@typescript-eslint/member-ordering`] |
+| [`no-any`] | ✅ | [`@typescript-eslint/no-explicit-any`] |
+| [`no-empty-interface`] | ✅ | [`@typescript-eslint/no-empty-object-type`] |
+| [`no-import-side-effect`] | 🔌 | [`import/no-unassigned-import`] |
+| [`no-inferrable-types`] | ✅ | [`@typescript-eslint/no-inferrable-types`] |
+| [`no-internal-module`] | ✅ | [`@typescript-eslint/prefer-namespace-keyword`] |
+| [`no-magic-numbers`] | ✅ | [`@typescript-eslint/no-magic-numbers`] |
+| [`no-namespace`] | ✅ | [`@typescript-eslint/no-namespace`] |
+| [`no-non-null-assertion`] | ✅ | [`@typescript-eslint/no-non-null-assertion`] |
+| [`no-parameter-reassignment`] | ✅ | [`no-param-reassign`][no-param-reassign] |
+| [`no-reference`] | ✅ | [`@typescript-eslint/triple-slash-reference`] |
+| [`no-unnecessary-type-assertion`] | ✅ | [`@typescript-eslint/no-unnecessary-type-assertion`] |
+| [`no-var-requires`] | ✅ | [`@typescript-eslint/no-var-requires`] |
+| [`only-arrow-functions`] | 🔌 | [`prefer-arrow/prefer-arrow-functions`] |
+| [`prefer-for-of`] | ✅ | [`@typescript-eslint/prefer-for-of`] |
+| [`promise-function-async`] | ✅ | [`@typescript-eslint/promise-function-async`] |
+| [`typedef-whitespace`] | ✅ | [`@typescript-eslint/type-annotation-spacing`] |
+| [`typedef`] | ✅ | [`@typescript-eslint/typedef`] |
+| [`unified-signatures`] | ✅ | [`@typescript-eslint/unified-signatures`] |
[1] The ESLint rule only supports exact string matching, rather than regular expressions
@@ -595,7 +595,7 @@ Relevant plugins: [`chai-expect-keywords`](https://github.com/gavinaiken/eslint-
[`@typescript-eslint/adjacent-overload-signatures`]: https://typescript-eslint.io/rules/adjacent-overload-signatures
[`@typescript-eslint/await-thenable`]: https://typescript-eslint.io/rules/await-thenable
-[`@typescript-eslint/ban-types`]: https://typescript-eslint.io/rules/ban-types
+[`@typescript-eslint/no-restricted-types`]: https://typescript-eslint.io/rules/no-restricted-types
[`@typescript-eslint/ban-ts-comment`]: https://typescript-eslint.io/rules/ban-ts-comment
[`@typescript-eslint/class-methods-use-this`]: https://typescript-eslint.io/rules/class-methods-use-this
[`@typescript-eslint/consistent-type-assertions`]: https://typescript-eslint.io/rules/consistent-type-assertions
diff --git a/packages/eslint-plugin/docs/rules/ban-types.md b/packages/eslint-plugin/docs/rules/ban-types.md
new file mode 100644
index 000000000000..f2aa717a07b2
--- /dev/null
+++ b/packages/eslint-plugin/docs/rules/ban-types.md
@@ -0,0 +1,22 @@
+:::danger Deprecated
+
+The old `ban-types` rule encompassed multiple areas of functionality, and so has been split into several rules.
+
+**[`no-restricted-types`](./no-restricted-types.mdx)** is the new rule for banning a configurable list of type names.
+It has no options enabled by default and is akin to rules like [`no-restricted-globals`](https://eslint.org/docs/latest/rules/no-restricted-globals), [`no-restricted-properties`](https://eslint.org/docs/latest/rules/no-restricted-properties), and [`no-restricted-syntax`](https://eslint.org/docs/latest/rules/no-restricted-syntax).
+
+The default options from `ban-types` are now covered by:
+
+- **[`no-empty-object-type`](./no-empty-object-type.mdx)**: banning the built-in `{}` type in confusing locations
+- **[`no-unsafe-function-type`](./no-unsafe-function-type.mdx)**: banning the built-in `Function`
+- **[`no-wrapper-object-types`](./no-wrapper-object-types.mdx)**: banning `Object` and built-in class wrappers such as `Number`
+
+`ban-types` itself is removed in typescript-eslint v8.
+See [Announcing typescript-eslint v8 Beta](/blog/announcing-typescript-eslint-v8-beta) for more details.
+:::
+
+
diff --git a/packages/eslint-plugin/docs/rules/ban-types.mdx b/packages/eslint-plugin/docs/rules/ban-types.mdx
deleted file mode 100644
index b52ae22df7d3..000000000000
--- a/packages/eslint-plugin/docs/rules/ban-types.mdx
+++ /dev/null
@@ -1,133 +0,0 @@
----
-description: 'Disallow certain types.'
----
-
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-
-> 🛑 This file is source code, not the primary documentation location! 🛑
->
-> See **https://typescript-eslint.io/rules/ban-types** for documentation.
-
-Some built-in types have aliases, while some types are considered dangerous or harmful.
-It's often a good idea to ban certain types to help with consistency and safety.
-
-This rule bans specific types and can suggest alternatives.
-Note that it does not ban the corresponding runtime objects from being used.
-
-## Examples
-
-Examples of code with the default options:
-
-
-
-
-```ts
-// use lower-case primitives for consistency
-const str: String = 'foo';
-const bool: Boolean = true;
-const num: Number = 1;
-const symb: Symbol = Symbol('foo');
-const bigInt: BigInt = 1n;
-
-// use a proper function type
-const func: Function = () => 1;
-
-// use safer object types
-const lowerObj: Object = {};
-const capitalObj: Object = { a: 'string' };
-```
-
-
-
-
-```ts
-// use lower-case primitives for consistency
-const str: string = 'foo';
-const bool: boolean = true;
-const num: number = 1;
-const symb: symbol = Symbol('foo');
-const bigInt: bigint = 1n;
-
-// use a proper function type
-const func: () => number = () => 1;
-
-// use safer object types
-const lowerObj: object = {};
-const capitalObj: { a: string } = { a: 'string' };
-```
-
-
-
-
-## Options
-
-The default options provide a set of "best practices", intended to provide safety and standardization in your codebase:
-
-- Don't use the upper-case primitive types or `Object`, you should use the lower-case types for consistency.
-- Avoid the `Function` type, as it provides little safety for the following reasons:
- - It provides no type safety when calling the value, which means it's easy to provide the wrong arguments.
- - It accepts class declarations, which will fail when called, as they are called without the `new` keyword.
-
-
-Default Options
-
-{/* Inject default options */}
-
-
-
-### `types`
-
-An object whose keys are the types you want to ban, and the values are error messages.
-
-The type can either be a type name literal (`Foo`), a type name with generic parameter instantiation(s) (`Foo`), the empty object literal (`{}`), or the empty tuple type (`[]`).
-
-The values can be:
-
-- A string, which is the error message to be reported; or
-- `false` to specifically un-ban this type (useful when you are using `extendDefaults`); or
-- An object with the following properties:
- - `message: string` - the message to display when the type is matched.
- - `fixWith?: string` - a string to replace the banned type with when the fixer is run. If this is omitted, no fix will be done.
- - `suggest?: string[]` - a list of suggested replacements for the banned type.
-
-### `extendDefaults`
-
-If you're specifying custom `types`, you can set this to `true` to extend the default `types` configuration. This is a convenience option to save you copying across the defaults when adding another type.
-
-If this is `false`, the rule will _only_ use the types defined in your configuration.
-
-Example configuration:
-
-```jsonc
-{
- "@typescript-eslint/ban-types": [
- "error",
- {
- "types": {
- // add a custom message to help explain why not to use it
- "Foo": "Don't use Foo because it is unsafe",
-
- // add a custom message, AND tell the plugin how to fix it
- "OldAPI": {
- "message": "Use NewAPI instead",
- "fixWith": "NewAPI",
- },
-
- // un-ban a type that's banned by default
- "{}": false,
- },
- "extendDefaults": true,
- },
- ],
-}
-```
-
-## When Not To Use It
-
-If your project is a rare one that intentionally deals with the class equivalents of primitives, it might not be worthwhile to enable the default `ban-types` options.
-You might consider using [ESLint disable comments](https://eslint.org/docs/latest/use/configure/rules#using-configuration-comments-1) for those specific situations instead of completely disabling this rule.
-
-## Related To
-
-- [`no-empty-object-type`](./no-empty-object-type.mdx)
diff --git a/packages/eslint-plugin/docs/rules/no-restricted-types.mdx b/packages/eslint-plugin/docs/rules/no-restricted-types.mdx
new file mode 100644
index 000000000000..20732174929e
--- /dev/null
+++ b/packages/eslint-plugin/docs/rules/no-restricted-types.mdx
@@ -0,0 +1,71 @@
+---
+description: 'Disallow certain types.'
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+> 🛑 This file is source code, not the primary documentation location! 🛑
+>
+> See **https://typescript-eslint.io/rules/no-restricted-types** for documentation.
+
+It can sometimes be useful to ban specific types from being used in type annotations.
+For example, a project might be migrating from using one type to another, and want to ban references to the old type.
+
+This rule can be configured to ban a list of specific types and can suggest alternatives.
+Note that it does not ban the corresponding runtime objects from being used.
+
+## Options
+
+### `types`
+
+An object whose keys are the types you want to ban, and the values are error messages.
+
+The type can either be a type name literal (`OldType`) or a a type name with generic parameter instantiation(s) (`OldType`).
+
+The values can be:
+
+- A string, which is the error message to be reported; or
+- `false` to specifically un-ban this type (useful when you are using `extendDefaults`); or
+- An object with the following properties:
+ - `message: string`: the message to display when the type is matched.
+ - `fixWith?: string`: a string to replace the banned type with when the fixer is run. If this is omitted, no fix will be done.
+ - `suggest?: string[]`: a list of suggested replacements for the banned type.
+
+Example configuration:
+
+```jsonc
+{
+ "@typescript-eslint/no-restricted-types": [
+ "error",
+ {
+ "types": {
+ // add a custom message to help explain why not to use it
+ "OldType": "Don't use OldType because it is unsafe",
+
+ // add a custom message, and tell the plugin how to fix it
+ "OldAPI": {
+ "message": "Use NewAPI instead",
+ "fixWith": "NewAPI",
+ },
+
+ // add a custom message, and tell the plugin how to suggest a fix
+ "SoonToBeOldAPI": {
+ "message": "Use NewAPI instead",
+ "suggest": ["NewAPIOne", "NewAPITwo"],
+ },
+ },
+ },
+ ],
+}
+```
+
+## When Not To Use It
+
+If you have no need to ban specific types from being used in type annotations, you don't need this rule.
+
+## Related To
+
+- [`no-empty-object-type`](./no-empty-object-type.mdx)
+- [`no-unsafe-function-type`](./no-unsafe-function-type.mdx)
+- [`no-wrapper-object-types`](./no-wrapper-object-types.mdx)
diff --git a/packages/eslint-plugin/docs/rules/no-unsafe-function-type.mdx b/packages/eslint-plugin/docs/rules/no-unsafe-function-type.mdx
new file mode 100644
index 000000000000..ea7b60794e4e
--- /dev/null
+++ b/packages/eslint-plugin/docs/rules/no-unsafe-function-type.mdx
@@ -0,0 +1,63 @@
+---
+description: 'Disallow using the unsafe built-in Function type.'
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+> 🛑 This file is source code, not the primary documentation location! 🛑
+>
+> See **https://typescript-eslint.io/rules/no-unsafe-function-type** for documentation.
+
+TypeScript's built-in `Function` type allows being called with any number of arguments and returns type `any`.
+`Function` also allows classes or plain objects that happen to possess all properties of the `Function` class.
+It's generally better to specify function parameters and return types with the function type syntax.
+
+"Catch-all" function types include:
+
+- `() => void`: a function that has no parameters and whose return is ignored
+- `(...args: never) => unknown`: a "top type" for functions that can be assigned any function type, but can't be called
+
+Examples of code for this rule:
+
+
+
+
+```ts
+let noParametersOrReturn: Function;
+noParametersOrReturn = () => {};
+
+let stringToNumber: Function;
+stringToNumber = (text: string) => text.length;
+
+let identity: Function;
+identity = value => value;
+```
+
+
+
+
+```ts
+let noParametersOrReturn: () => void;
+noParametersOrReturn = () => {};
+
+let stringToNumber: (text: string) => number;
+stringToNumber = text => text.length;
+
+let identity: (value: T) => T;
+identity = value => value;
+```
+
+
+
+
+## When Not To Use It
+
+If your project is still onboarding to TypeScript, it might be difficult to fully replace all unsafe `Function` types with more precise function types.
+You might consider using [ESLint disable comments](https://eslint.org/docs/latest/use/configure/rules#using-configuration-comments-1) for those specific situations instead of completely disabling this rule.
+
+## Related To
+
+- [`no-empty-object-type`](./no-empty-object-type.mdx)
+- [`no-restricted-types`](./no-restricted-types.mdx)
+- [`no-wrapper-object-types`](./no-wrapper-object-types.mdx)
diff --git a/packages/eslint-plugin/docs/rules/no-wrapper-object-types.mdx b/packages/eslint-plugin/docs/rules/no-wrapper-object-types.mdx
new file mode 100644
index 000000000000..858802470cbb
--- /dev/null
+++ b/packages/eslint-plugin/docs/rules/no-wrapper-object-types.mdx
@@ -0,0 +1,67 @@
+---
+description: 'Disallow using confusing built-in primitive class wrappers.'
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+> 🛑 This file is source code, not the primary documentation location! 🛑
+>
+> See **https://typescript-eslint.io/rules/no-wrapper-object-types** for documentation.
+
+The JavaScript language has a set of language types, but some of them correspond to two TypeScript types, which look similar: `boolean`/`Boolean`, `number`/`Number`, `string`/`String`, `bigint`/`BigInt`, `symbol`/`Symbol`, `object`/`Object`.
+The difference is that the lowercase variants are compiler intrinsics and specify the actual _runtime types_ (that is, the return value when you use the `typeof` operator), while the uppercase variants are _structural types_ defined in the library that can be satisfied by any user-defined object with the right properties, not just the real primitives.
+JavaScript also has a "wrapper" class object for each of those primitives, such as [`Boolean`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean) and [`Number`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number).
+These wrapper objects are assignable to the uppercase types, but not to the lowercase types.
+
+Using the primitives like `0` instead of object wrappers like `new Number(0)` is generally considered a JavaScript best practice.
+JavaScript programs typically work with the real number primitives, rather than objects that "look like" numbers.
+Primitives are simpler to conceptualize and work with `==` and `===` equality checks -- which their object equivalents do notDeepEqual.
+As a result, using the lowercase type names like `number` instead of the uppercase names like `Number` helps make your code behave more reliably.
+
+Examples of code for this rule:
+
+
+
+
+```ts
+let myBigInt: BigInt;
+let myBoolean: Boolean;
+let myNumber: Number;
+let myString: String;
+let mySymbol: Symbol;
+
+let myObject: Object = 'allowed by TypeScript';
+```
+
+
+
+
+```ts
+let myBigint: bigint;
+let myBoolean: boolean;
+let myNumber: number;
+let myString: string;
+let mySymbol: symbol;
+
+let myObject: object = "Type 'string' is not assignable to type 'object'.";
+```
+
+
+
+
+## When Not To Use It
+
+If your project is a rare one that intentionally deals with the class equivalents of primitives, it might not be worthwhile to use this rule.
+You might consider using [ESLint disable comments](https://eslint.org/docs/latest/use/configure/rules#using-configuration-comments-1) for those specific situations instead of completely disabling this rule.
+
+## Further Reading
+
+- [MDN documentation on primitives](https://developer.mozilla.org/en-US/docs/Glossary/Primitive)
+- [MDN documentation on `string` primitives and `String` objects](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String#string_primitives_and_string_objects)
+
+## Related To
+
+- [`no-empty-object-type`](./no-empty-object-type.mdx)
+- [`no-restricted-types`](./no-restricted-types.mdx)
+- [`no-unsafe-function-type`](./no-unsafe-function-type.mdx)
diff --git a/packages/eslint-plugin/src/configs/all.ts b/packages/eslint-plugin/src/configs/all.ts
index 5c1e1d7725ac..ab24d6943de0 100644
--- a/packages/eslint-plugin/src/configs/all.ts
+++ b/packages/eslint-plugin/src/configs/all.ts
@@ -15,7 +15,6 @@ export = {
'@typescript-eslint/await-thenable': 'error',
'@typescript-eslint/ban-ts-comment': 'error',
'@typescript-eslint/ban-tslint-comment': 'error',
- '@typescript-eslint/ban-types': 'error',
'@typescript-eslint/class-literal-property-style': 'error',
'class-methods-use-this': 'off',
'@typescript-eslint/class-methods-use-this': 'error',
@@ -85,6 +84,7 @@ export = {
'@typescript-eslint/no-require-imports': 'error',
'no-restricted-imports': 'off',
'@typescript-eslint/no-restricted-imports': 'error',
+ '@typescript-eslint/no-restricted-types': 'error',
'no-shadow': 'off',
'@typescript-eslint/no-shadow': 'error',
'@typescript-eslint/no-this-alias': 'error',
@@ -100,6 +100,7 @@ export = {
'@typescript-eslint/no-unsafe-call': 'error',
'@typescript-eslint/no-unsafe-declaration-merging': 'error',
'@typescript-eslint/no-unsafe-enum-comparison': 'error',
+ '@typescript-eslint/no-unsafe-function-type': 'error',
'@typescript-eslint/no-unsafe-member-access': 'error',
'@typescript-eslint/no-unsafe-return': 'error',
'@typescript-eslint/no-unsafe-unary-minus': 'error',
@@ -112,6 +113,7 @@ export = {
'no-useless-constructor': 'off',
'@typescript-eslint/no-useless-constructor': 'error',
'@typescript-eslint/no-useless-empty-export': 'error',
+ '@typescript-eslint/no-wrapper-object-types': 'error',
'@typescript-eslint/non-nullable-type-assertion-style': 'error',
'no-throw-literal': 'off',
'@typescript-eslint/only-throw-error': 'error',
diff --git a/packages/eslint-plugin/src/configs/recommended-type-checked.ts b/packages/eslint-plugin/src/configs/recommended-type-checked.ts
index d0975fddfe8b..e8e10a7071a5 100644
--- a/packages/eslint-plugin/src/configs/recommended-type-checked.ts
+++ b/packages/eslint-plugin/src/configs/recommended-type-checked.ts
@@ -12,7 +12,6 @@ export = {
rules: {
'@typescript-eslint/await-thenable': 'error',
'@typescript-eslint/ban-ts-comment': 'error',
- '@typescript-eslint/ban-types': 'error',
'no-array-constructor': 'off',
'@typescript-eslint/no-array-constructor': 'error',
'@typescript-eslint/no-array-delete': 'error',
@@ -40,6 +39,7 @@ export = {
'@typescript-eslint/no-unsafe-call': 'error',
'@typescript-eslint/no-unsafe-declaration-merging': 'error',
'@typescript-eslint/no-unsafe-enum-comparison': 'error',
+ '@typescript-eslint/no-unsafe-function-type': 'error',
'@typescript-eslint/no-unsafe-member-access': 'error',
'@typescript-eslint/no-unsafe-return': 'error',
'@typescript-eslint/no-unsafe-unary-minus': 'error',
@@ -47,6 +47,7 @@ export = {
'@typescript-eslint/no-unused-expressions': 'error',
'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': 'error',
+ '@typescript-eslint/no-wrapper-object-types': 'error',
'no-throw-literal': 'off',
'@typescript-eslint/only-throw-error': 'error',
'@typescript-eslint/prefer-as-const': 'error',
diff --git a/packages/eslint-plugin/src/configs/recommended.ts b/packages/eslint-plugin/src/configs/recommended.ts
index 6657edaaf5b9..487935ee1f23 100644
--- a/packages/eslint-plugin/src/configs/recommended.ts
+++ b/packages/eslint-plugin/src/configs/recommended.ts
@@ -11,7 +11,6 @@ export = {
extends: ['./configs/base', './configs/eslint-recommended'],
rules: {
'@typescript-eslint/ban-ts-comment': 'error',
- '@typescript-eslint/ban-types': 'error',
'no-array-constructor': 'off',
'@typescript-eslint/no-array-constructor': 'error',
'@typescript-eslint/no-duplicate-enum-values': 'error',
@@ -25,10 +24,12 @@ export = {
'@typescript-eslint/no-this-alias': 'error',
'@typescript-eslint/no-unnecessary-type-constraint': 'error',
'@typescript-eslint/no-unsafe-declaration-merging': 'error',
+ '@typescript-eslint/no-unsafe-function-type': 'error',
'no-unused-expressions': 'off',
'@typescript-eslint/no-unused-expressions': 'error',
'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': 'error',
+ '@typescript-eslint/no-wrapper-object-types': 'error',
'@typescript-eslint/prefer-as-const': 'error',
'@typescript-eslint/prefer-namespace-keyword': 'error',
'@typescript-eslint/triple-slash-reference': 'error',
diff --git a/packages/eslint-plugin/src/configs/strict-type-checked.ts b/packages/eslint-plugin/src/configs/strict-type-checked.ts
index 7987868204db..11d65130de62 100644
--- a/packages/eslint-plugin/src/configs/strict-type-checked.ts
+++ b/packages/eslint-plugin/src/configs/strict-type-checked.ts
@@ -15,7 +15,6 @@ export = {
'error',
{ minimumDescriptionLength: 10 },
],
- '@typescript-eslint/ban-types': 'error',
'no-array-constructor': 'off',
'@typescript-eslint/no-array-constructor': 'error',
'@typescript-eslint/no-array-delete': 'error',
@@ -55,6 +54,7 @@ export = {
'@typescript-eslint/no-unsafe-call': 'error',
'@typescript-eslint/no-unsafe-declaration-merging': 'error',
'@typescript-eslint/no-unsafe-enum-comparison': 'error',
+ '@typescript-eslint/no-unsafe-function-type': 'error',
'@typescript-eslint/no-unsafe-member-access': 'error',
'@typescript-eslint/no-unsafe-return': 'error',
'@typescript-eslint/no-unsafe-unary-minus': 'error',
@@ -64,6 +64,7 @@ export = {
'@typescript-eslint/no-unused-vars': 'error',
'no-useless-constructor': 'off',
'@typescript-eslint/no-useless-constructor': 'error',
+ '@typescript-eslint/no-wrapper-object-types': 'error',
'no-throw-literal': 'off',
'@typescript-eslint/only-throw-error': 'error',
'@typescript-eslint/prefer-as-const': 'error',
diff --git a/packages/eslint-plugin/src/configs/strict.ts b/packages/eslint-plugin/src/configs/strict.ts
index fc7cb1febebc..0e655d1464ca 100644
--- a/packages/eslint-plugin/src/configs/strict.ts
+++ b/packages/eslint-plugin/src/configs/strict.ts
@@ -14,7 +14,6 @@ export = {
'error',
{ minimumDescriptionLength: 10 },
],
- '@typescript-eslint/ban-types': 'error',
'no-array-constructor': 'off',
'@typescript-eslint/no-array-constructor': 'error',
'@typescript-eslint/no-duplicate-enum-values': 'error',
@@ -33,12 +32,14 @@ export = {
'@typescript-eslint/no-this-alias': 'error',
'@typescript-eslint/no-unnecessary-type-constraint': 'error',
'@typescript-eslint/no-unsafe-declaration-merging': 'error',
+ '@typescript-eslint/no-unsafe-function-type': 'error',
'no-unused-expressions': 'off',
'@typescript-eslint/no-unused-expressions': 'error',
'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': 'error',
'no-useless-constructor': 'off',
'@typescript-eslint/no-useless-constructor': 'error',
+ '@typescript-eslint/no-wrapper-object-types': 'error',
'@typescript-eslint/prefer-as-const': 'error',
'@typescript-eslint/prefer-literal-enum-member': 'error',
'@typescript-eslint/prefer-namespace-keyword': 'error',
diff --git a/packages/eslint-plugin/src/rules/index.ts b/packages/eslint-plugin/src/rules/index.ts
index c167013211c0..d11366a54341 100644
--- a/packages/eslint-plugin/src/rules/index.ts
+++ b/packages/eslint-plugin/src/rules/index.ts
@@ -5,7 +5,6 @@ import arrayType from './array-type';
import awaitThenable from './await-thenable';
import banTsComment from './ban-ts-comment';
import banTslintComment from './ban-tslint-comment';
-import banTypes from './ban-types';
import classLiteralPropertyStyle from './class-literal-property-style';
import classMethodsUseThis from './class-methods-use-this';
import consistentGenericConstructors from './consistent-generic-constructors';
@@ -62,6 +61,7 @@ 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 noRestrictedTypes from './no-restricted-types';
import noShadow from './no-shadow';
import noThisAlias from './no-this-alias';
import noTypeAlias from './no-type-alias';
@@ -77,6 +77,7 @@ import noUnsafeAssignment from './no-unsafe-assignment';
import noUnsafeCall from './no-unsafe-call';
import noUnsafeDeclarationMerging from './no-unsafe-declaration-merging';
import noUnsafeEnumComparison from './no-unsafe-enum-comparison';
+import noUnsafeFunctionType from './no-unsafe-function-type';
import noUnsafeMemberAccess from './no-unsafe-member-access';
import noUnsafeReturn from './no-unsafe-return';
import noUnsafeUnaryMinus from './no-unsafe-unary-minus';
@@ -86,6 +87,7 @@ 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 noWrapperObjectTypes from './no-wrapper-object-types';
import nonNullableTypeAssertionStyle from './non-nullable-type-assertion-style';
import onlyThrowError from './only-throw-error';
import parameterProperties from './parameter-properties';
@@ -129,7 +131,6 @@ export default {
'await-thenable': awaitThenable,
'ban-ts-comment': banTsComment,
'ban-tslint-comment': banTslintComment,
- 'ban-types': banTypes,
'class-literal-property-style': classLiteralPropertyStyle,
'class-methods-use-this': classMethodsUseThis,
'consistent-generic-constructors': consistentGenericConstructors,
@@ -186,6 +187,7 @@ export default {
'no-redundant-type-constituents': noRedundantTypeConstituents,
'no-require-imports': noRequireImports,
'no-restricted-imports': noRestrictedImports,
+ 'no-restricted-types': noRestrictedTypes,
'no-shadow': noShadow,
'no-this-alias': noThisAlias,
'no-type-alias': noTypeAlias,
@@ -201,6 +203,7 @@ export default {
'no-unsafe-call': noUnsafeCall,
'no-unsafe-declaration-merging': noUnsafeDeclarationMerging,
'no-unsafe-enum-comparison': noUnsafeEnumComparison,
+ 'no-unsafe-function-type': noUnsafeFunctionType,
'no-unsafe-member-access': noUnsafeMemberAccess,
'no-unsafe-return': noUnsafeReturn,
'no-unsafe-unary-minus': noUnsafeUnaryMinus,
@@ -210,6 +213,7 @@ export default {
'no-useless-constructor': noUselessConstructor,
'no-useless-empty-export': noUselessEmptyExport,
'no-var-requires': noVarRequires,
+ 'no-wrapper-object-types': noWrapperObjectTypes,
'non-nullable-type-assertion-style': nonNullableTypeAssertionStyle,
'only-throw-error': onlyThrowError,
'parameter-properties': parameterProperties,
diff --git a/packages/eslint-plugin/src/rules/no-implied-eval.ts b/packages/eslint-plugin/src/rules/no-implied-eval.ts
index dee23d62d275..b2bfac55203a 100644
--- a/packages/eslint-plugin/src/rules/no-implied-eval.ts
+++ b/packages/eslint-plugin/src/rules/no-implied-eval.ts
@@ -3,7 +3,11 @@ import { AST_NODE_TYPES } from '@typescript-eslint/utils';
import * as tsutils from 'ts-api-utils';
import * as ts from 'typescript';
-import { createRule, getParserServices } from '../util';
+import {
+ createRule,
+ getParserServices,
+ isReferenceToGlobalFunction,
+} from '../util';
const FUNCTION_CONSTRUCTOR = 'Function';
const GLOBAL_CANDIDATES = new Set(['global', 'window', 'globalThis']);
@@ -119,18 +123,6 @@ export default createRule({
}
}
- function isReferenceToGlobalFunction(
- calleeName: string,
- node: TSESTree.Node,
- ): boolean {
- const ref = context.sourceCode
- .getScope(node)
- .references.find(ref => ref.identifier.name === calleeName);
-
- // ensure it's the "global" version
- return !ref?.resolved || ref.resolved.defs.length === 0;
- }
-
function checkImpliedEval(
node: TSESTree.CallExpression | TSESTree.NewExpression,
): void {
@@ -165,7 +157,7 @@ export default createRule({
if (
EVAL_LIKE_METHODS.has(calleeName) &&
!isFunction(handler) &&
- isReferenceToGlobalFunction(calleeName, node)
+ isReferenceToGlobalFunction(calleeName, node, context.sourceCode)
) {
context.report({ node: handler, messageId: 'noImpliedEvalError' });
}
diff --git a/packages/eslint-plugin/src/rules/ban-types.ts b/packages/eslint-plugin/src/rules/no-restricted-types.ts
similarity index 74%
rename from packages/eslint-plugin/src/rules/ban-types.ts
rename to packages/eslint-plugin/src/rules/no-restricted-types.ts
index 06c9daa62d4a..f3d2e3b3f197 100644
--- a/packages/eslint-plugin/src/rules/ban-types.ts
+++ b/packages/eslint-plugin/src/rules/no-restricted-types.ts
@@ -21,6 +21,7 @@ export type Options = [
extendDefaults?: boolean;
},
];
+
export type MessageIds = 'bannedTypeMessage' | 'bannedTypeReplacement';
function removeSpaces(str: string): string {
@@ -37,7 +38,7 @@ function stringifyNode(
function getCustomMessage(
bannedType: string | true | { message?: string; fixWith?: string } | null,
): string {
- if (bannedType == null || bannedType === true) {
+ if (!bannedType || bannedType === true) {
return '';
}
@@ -52,42 +53,7 @@ function getCustomMessage(
return '';
}
-const defaultTypes: Types = {
- String: {
- message: 'Use string instead',
- fixWith: 'string',
- },
- Boolean: {
- message: 'Use boolean instead',
- fixWith: 'boolean',
- },
- Number: {
- message: 'Use number instead',
- fixWith: 'number',
- },
- Symbol: {
- message: 'Use symbol instead',
- fixWith: 'symbol',
- },
- BigInt: {
- message: 'Use bigint instead',
- fixWith: 'bigint',
- },
- Object: {
- message: 'Use object instead',
- fixWith: 'object',
- },
- Function: {
- message: [
- 'The `Function` type accepts any function-like value.',
- 'It provides no type safety when calling the function, which can be a common source of bugs.',
- 'It also accepts things like class declarations, which will throw at runtime as they will not be called with `new`.',
- 'If you are expecting the function to accept certain arguments, you should explicitly define the function shape.',
- ].join('\n'),
- },
-};
-
-export const TYPE_KEYWORDS = {
+const TYPE_KEYWORDS = {
bigint: AST_NODE_TYPES.TSBigIntKeyword,
boolean: AST_NODE_TYPES.TSBooleanKeyword,
never: AST_NODE_TYPES.TSNeverKeyword,
@@ -102,12 +68,11 @@ export const TYPE_KEYWORDS = {
};
export default createRule({
- name: 'ban-types',
+ name: 'no-restricted-types',
meta: {
type: 'suggestion',
docs: {
description: 'Disallow certain types',
- recommended: 'recommended',
},
fixable: 'code',
hasSuggestions: true,
@@ -120,16 +85,6 @@ export default createRule({
$defs: {
banConfig: {
oneOf: [
- {
- type: 'null',
- description: 'Bans the type with the default message',
- },
- {
- type: 'boolean',
- enum: [false],
- description:
- 'Un-bans the type (useful when paired with `extendDefaults`)',
- },
{
type: 'boolean',
enum: [true],
@@ -156,7 +111,6 @@ export default createRule({
type: 'array',
items: { type: 'string' },
description: 'Types to suggest replacing with.',
- additionalItems: false,
},
},
additionalProperties: false,
@@ -172,23 +126,13 @@ export default createRule({
$ref: '#/items/0/$defs/banConfig',
},
},
- extendDefaults: {
- type: 'boolean',
- },
},
additionalProperties: false,
},
],
},
defaultOptions: [{}],
- create(context, [options]) {
- const extendDefaults = options.extendDefaults ?? true;
- const customTypes = options.types ?? {};
- const types = Object.assign(
- {},
- extendDefaults ? defaultTypes : {},
- customTypes,
- );
+ create(context, [{ types = {} }]) {
const bannedTypes = new Map(
Object.entries(types).map(([type, data]) => [removeSpaces(type), data]),
);
@@ -249,15 +193,19 @@ export default createRule({
return {
...keywordSelectors,
- TSTypeLiteral(node): void {
- if (node.members.length) {
- return;
- }
-
+ TSClassImplements(node): void {
+ checkBannedTypes(node);
+ },
+ TSInterfaceHeritage(node): void {
checkBannedTypes(node);
},
TSTupleType(node): void {
- if (node.elementTypes.length === 0) {
+ if (!node.elementTypes.length) {
+ checkBannedTypes(node);
+ }
+ },
+ TSTypeLiteral(node): void {
+ if (!node.members.length) {
checkBannedTypes(node);
}
},
@@ -268,12 +216,6 @@ export default createRule({
checkBannedTypes(node);
}
},
- TSInterfaceHeritage(node): void {
- checkBannedTypes(node);
- },
- TSClassImplements(node): void {
- checkBannedTypes(node);
- },
};
},
});
diff --git a/packages/eslint-plugin/src/rules/no-unsafe-function-type.ts b/packages/eslint-plugin/src/rules/no-unsafe-function-type.ts
new file mode 100644
index 000000000000..624c038f8ff7
--- /dev/null
+++ b/packages/eslint-plugin/src/rules/no-unsafe-function-type.ts
@@ -0,0 +1,50 @@
+import type { TSESTree } from '@typescript-eslint/utils';
+import { AST_NODE_TYPES } from '@typescript-eslint/utils';
+
+import { createRule, isReferenceToGlobalFunction } from '../util';
+
+export default createRule({
+ name: 'no-unsafe-function-type',
+ meta: {
+ type: 'problem',
+ docs: {
+ description: 'Disallow using the unsafe built-in Function type',
+ recommended: 'recommended',
+ },
+ fixable: 'code',
+ messages: {
+ bannedFunctionType: [
+ 'The `Function` type accepts any function-like value.',
+ 'Prefer explicitly defining any function parameters and return type.',
+ ].join('\n'),
+ },
+ schema: [],
+ },
+ defaultOptions: [],
+ create(context) {
+ function checkBannedTypes(node: TSESTree.Node): void {
+ if (
+ node.type === AST_NODE_TYPES.Identifier &&
+ node.name === 'Function' &&
+ isReferenceToGlobalFunction('Function', node, context.sourceCode)
+ ) {
+ context.report({
+ node,
+ messageId: 'bannedFunctionType',
+ });
+ }
+ }
+
+ return {
+ TSClassImplements(node): void {
+ checkBannedTypes(node.expression);
+ },
+ TSInterfaceHeritage(node): void {
+ checkBannedTypes(node.expression);
+ },
+ TSTypeReference(node): void {
+ checkBannedTypes(node.typeName);
+ },
+ };
+ },
+});
diff --git a/packages/eslint-plugin/src/rules/no-wrapper-object-types.ts b/packages/eslint-plugin/src/rules/no-wrapper-object-types.ts
new file mode 100644
index 000000000000..f51b6c8564b5
--- /dev/null
+++ b/packages/eslint-plugin/src/rules/no-wrapper-object-types.ts
@@ -0,0 +1,71 @@
+import type { TSESLint, TSESTree } from '@typescript-eslint/utils';
+import { AST_NODE_TYPES } from '@typescript-eslint/utils';
+
+import { createRule, isReferenceToGlobalFunction } from '../util';
+
+const classNames = new Set([
+ 'BigInt',
+ // eslint-disable-next-line @typescript-eslint/internal/prefer-ast-types-enum
+ 'Boolean',
+ 'Number',
+ 'Object',
+ // eslint-disable-next-line @typescript-eslint/internal/prefer-ast-types-enum
+ 'String',
+ 'Symbol',
+]);
+
+export default createRule({
+ name: 'no-wrapper-object-types',
+ meta: {
+ type: 'problem',
+ docs: {
+ description: 'Disallow using confusing built-in primitive class wrappers',
+ recommended: 'recommended',
+ },
+ fixable: 'code',
+ messages: {
+ bannedClassType:
+ 'Prefer using the primitive `{{preferred}}` as a type name, rather than the upper-cased `{{typeName}}`.',
+ },
+ schema: [],
+ },
+ defaultOptions: [],
+ create(context) {
+ function checkBannedTypes(
+ node: TSESTree.EntityName | TSESTree.Expression,
+ includeFix: boolean,
+ ): void {
+ const typeName = node.type === AST_NODE_TYPES.Identifier && node.name;
+ if (
+ !typeName ||
+ !classNames.has(typeName) ||
+ !isReferenceToGlobalFunction(typeName, node, context.sourceCode)
+ ) {
+ return;
+ }
+
+ const preferred = typeName.toLowerCase();
+
+ context.report({
+ data: { typeName, preferred },
+ fix: includeFix
+ ? (fixer): TSESLint.RuleFix => fixer.replaceText(node, preferred)
+ : undefined,
+ messageId: 'bannedClassType',
+ node,
+ });
+ }
+
+ return {
+ TSClassImplements(node): void {
+ checkBannedTypes(node.expression, false);
+ },
+ TSInterfaceHeritage(node): void {
+ checkBannedTypes(node.expression, false);
+ },
+ TSTypeReference(node): void {
+ checkBannedTypes(node.typeName, true);
+ },
+ };
+ },
+});
diff --git a/packages/eslint-plugin/src/rules/prefer-optional-chain-utils/gatherLogicalOperands.ts b/packages/eslint-plugin/src/rules/prefer-optional-chain-utils/gatherLogicalOperands.ts
index 54c44f4edda9..53beee5d2826 100644
--- a/packages/eslint-plugin/src/rules/prefer-optional-chain-utils/gatherLogicalOperands.ts
+++ b/packages/eslint-plugin/src/rules/prefer-optional-chain-utils/gatherLogicalOperands.ts
@@ -13,7 +13,7 @@ import {
} from 'ts-api-utils';
import * as ts from 'typescript';
-import { isTypeFlagSet } from '../../util';
+import { isReferenceToGlobalFunction, isTypeFlagSet } from '../../util';
import type { PreferOptionalChainOptions } from './PreferOptionalChainOptions';
const enum ComparisonValueType {
@@ -153,16 +153,13 @@ export function gatherLogicalOperands(
comparedExpression.operator === 'typeof'
) {
const argument = comparedExpression.argument;
- if (argument.type === AST_NODE_TYPES.Identifier) {
- const reference = sourceCode
- .getScope(argument)
- .references.find(ref => ref.identifier.name === argument.name);
-
- if (!reference?.resolved?.defs.length) {
- // typeof window === 'undefined'
- result.push({ type: OperandValidity.Invalid });
- continue;
- }
+ if (
+ argument.type === AST_NODE_TYPES.Identifier &&
+ // typeof window === 'undefined'
+ isReferenceToGlobalFunction(argument.name, argument, sourceCode)
+ ) {
+ result.push({ type: OperandValidity.Invalid });
+ continue;
}
// typeof x.y === 'undefined'
diff --git a/packages/eslint-plugin/src/util/index.ts b/packages/eslint-plugin/src/util/index.ts
index 1f8d657cd542..083353a69233 100644
--- a/packages/eslint-plugin/src/util/index.ts
+++ b/packages/eslint-plugin/src/util/index.ts
@@ -14,6 +14,7 @@ export * from './isNullLiteral';
export * from './isUndefinedIdentifier';
export * from './misc';
export * from './objectIterators';
+export * from './scopeUtils';
export * from './types';
export * from './isAssignee';
diff --git a/packages/eslint-plugin/src/util/scopeUtils.ts b/packages/eslint-plugin/src/util/scopeUtils.ts
new file mode 100644
index 000000000000..b350a7412106
--- /dev/null
+++ b/packages/eslint-plugin/src/util/scopeUtils.ts
@@ -0,0 +1,15 @@
+import type { TSESTree } from '@typescript-eslint/utils';
+import type { SourceCode } from '@typescript-eslint/utils/ts-eslint';
+
+export function isReferenceToGlobalFunction(
+ calleeName: string,
+ node: TSESTree.Node,
+ sourceCode: SourceCode,
+): boolean {
+ const ref = sourceCode
+ .getScope(node)
+ .references.find(ref => ref.identifier.name === calleeName);
+
+ // ensure it's the "global" version
+ return !ref?.resolved?.defs.length;
+}
diff --git a/packages/eslint-plugin/tests/docs-eslint-output-snapshots/ban-types.shot b/packages/eslint-plugin/tests/docs-eslint-output-snapshots/ban-types.shot
deleted file mode 100644
index 6644fd985d9b..000000000000
--- a/packages/eslint-plugin/tests/docs-eslint-output-snapshots/ban-types.shot
+++ /dev/null
@@ -1,50 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Validating rule docs ban-types.mdx code examples ESLint output 1`] = `
-"Incorrect
-
-// use lower-case primitives for consistency
-const str: String = 'foo';
- ~~~~~~ Don't use \`String\` as a type. Use string instead
-const bool: Boolean = true;
- ~~~~~~~ Don't use \`Boolean\` as a type. Use boolean instead
-const num: Number = 1;
- ~~~~~~ Don't use \`Number\` as a type. Use number instead
-const symb: Symbol = Symbol('foo');
- ~~~~~~ Don't use \`Symbol\` as a type. Use symbol instead
-const bigInt: BigInt = 1n;
- ~~~~~~ Don't use \`BigInt\` as a type. Use bigint instead
-
-// use a proper function type
-const func: Function = () => 1;
- ~~~~~~~~ Don't use \`Function\` as a type. The \`Function\` type accepts any function-like value.
- It provides no type safety when calling the function, which can be a common source of bugs.
- It also accepts things like class declarations, which will throw at runtime as they will not be called with \`new\`.
- If you are expecting the function to accept certain arguments, you should explicitly define the function shape.
-
-// use safer object types
-const lowerObj: Object = {};
- ~~~~~~ Don't use \`Object\` as a type. Use object instead
-const capitalObj: Object = { a: 'string' };
- ~~~~~~ Don't use \`Object\` as a type. Use object instead
-"
-`;
-
-exports[`Validating rule docs ban-types.mdx code examples ESLint output 2`] = `
-"Correct
-
-// use lower-case primitives for consistency
-const str: string = 'foo';
-const bool: boolean = true;
-const num: number = 1;
-const symb: symbol = Symbol('foo');
-const bigInt: bigint = 1n;
-
-// use a proper function type
-const func: () => number = () => 1;
-
-// use safer object types
-const lowerObj: object = {};
-const capitalObj: { a: string } = { a: 'string' };
-"
-`;
diff --git a/packages/eslint-plugin/tests/docs-eslint-output-snapshots/no-unsafe-function-type.shot b/packages/eslint-plugin/tests/docs-eslint-output-snapshots/no-unsafe-function-type.shot
new file mode 100644
index 000000000000..c77f15d6feb3
--- /dev/null
+++ b/packages/eslint-plugin/tests/docs-eslint-output-snapshots/no-unsafe-function-type.shot
@@ -0,0 +1,35 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Validating rule docs no-unsafe-function-type.mdx code examples ESLint output 1`] = `
+"Incorrect
+
+let noParametersOrReturn: Function;
+ ~~~~~~~~ The \`Function\` type accepts any function-like value.
+ Prefer explicitly defining any function parameters and return type.
+noParametersOrReturn = () => {};
+
+let stringToNumber: Function;
+ ~~~~~~~~ The \`Function\` type accepts any function-like value.
+ Prefer explicitly defining any function parameters and return type.
+stringToNumber = (text: string) => text.length;
+
+let identity: Function;
+ ~~~~~~~~ The \`Function\` type accepts any function-like value.
+ Prefer explicitly defining any function parameters and return type.
+identity = value => value;
+"
+`;
+
+exports[`Validating rule docs no-unsafe-function-type.mdx code examples ESLint output 2`] = `
+"Correct
+
+let noParametersOrReturn: () => void;
+noParametersOrReturn = () => {};
+
+let stringToNumber: (text: string) => number;
+stringToNumber = text => text.length;
+
+let identity: (value: T) => T;
+identity = value => value;
+"
+`;
diff --git a/packages/eslint-plugin/tests/docs-eslint-output-snapshots/no-wrapper-object-types.shot b/packages/eslint-plugin/tests/docs-eslint-output-snapshots/no-wrapper-object-types.shot
new file mode 100644
index 000000000000..4303b79a77a7
--- /dev/null
+++ b/packages/eslint-plugin/tests/docs-eslint-output-snapshots/no-wrapper-object-types.shot
@@ -0,0 +1,33 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Validating rule docs no-wrapper-object-types.mdx code examples ESLint output 1`] = `
+"Incorrect
+
+let myBigInt: BigInt;
+ ~~~~~~ Prefer using the primitive \`bigint\` as a type name, rather than the upper-cased \`BigInt\`.
+let myBoolean: Boolean;
+ ~~~~~~~ Prefer using the primitive \`boolean\` as a type name, rather than the upper-cased \`Boolean\`.
+let myNumber: Number;
+ ~~~~~~ Prefer using the primitive \`number\` as a type name, rather than the upper-cased \`Number\`.
+let myString: String;
+ ~~~~~~ Prefer using the primitive \`string\` as a type name, rather than the upper-cased \`String\`.
+let mySymbol: Symbol;
+ ~~~~~~ Prefer using the primitive \`symbol\` as a type name, rather than the upper-cased \`Symbol\`.
+
+let myObject: Object = 'allowed by TypeScript';
+ ~~~~~~ Prefer using the primitive \`object\` as a type name, rather than the upper-cased \`Object\`.
+"
+`;
+
+exports[`Validating rule docs no-wrapper-object-types.mdx code examples ESLint output 2`] = `
+"Correct
+
+let myBigint: bigint;
+let myBoolean: boolean;
+let myNumber: number;
+let myString: string;
+let mySymbol: symbol;
+
+let myObject: object = "Type 'string' is not assignable to type 'object'.";
+"
+`;
diff --git a/packages/eslint-plugin/tests/docs.test.ts b/packages/eslint-plugin/tests/docs.test.ts
index 96401ecb2fa5..a54f57a7bee4 100644
--- a/packages/eslint-plugin/tests/docs.test.ts
+++ b/packages/eslint-plugin/tests/docs.test.ts
@@ -155,6 +155,7 @@ describe('Validating rule docs', () => {
'TEMPLATE.md',
// These rule docs were left behind on purpose for legacy reasons. See the
// comments in the files for more information.
+ 'ban-types.md',
'no-duplicate-imports.mdx',
'no-parameter-properties.mdx',
'no-useless-template-literals.mdx',
@@ -162,7 +163,11 @@ describe('Validating rule docs', () => {
...oldStylisticRules,
]);
- const rulesWithComplexOptions = new Set(['array-type', 'member-ordering']);
+ const rulesWithComplexOptions = new Set([
+ 'array-type',
+ 'member-ordering',
+ 'no-restricted-types',
+ ]);
// TODO: whittle this list down to as few as possible
const rulesWithComplexOptionHeadings = new Set([
diff --git a/packages/eslint-plugin/tests/rules/ban-types.test.ts b/packages/eslint-plugin/tests/rules/ban-types.test.ts
deleted file mode 100644
index 046ac290a635..000000000000
--- a/packages/eslint-plugin/tests/rules/ban-types.test.ts
+++ /dev/null
@@ -1,741 +0,0 @@
-/* eslint-disable @typescript-eslint/internal/prefer-ast-types-enum */
-import { noFormat, RuleTester } from '@typescript-eslint/rule-tester';
-import type { TSESLint } from '@typescript-eslint/utils';
-
-import type { MessageIds, Options } from '../../src/rules/ban-types';
-import rule, { TYPE_KEYWORDS } from '../../src/rules/ban-types';
-import { objectReduceKey } from '../../src/util';
-
-const ruleTester = new RuleTester({
- parser: '@typescript-eslint/parser',
-});
-
-const options: Options = [
- {
- types: {
- String: {
- message: 'Use string instead.',
- fixWith: 'string',
- },
- Object: "Use '{}' instead.",
- Array: null,
- F: null,
- 'NS.Bad': {
- message: 'Use NS.Good instead.',
- fixWith: 'NS.Good',
- },
- },
- extendDefaults: false,
- },
-];
-
-ruleTester.run('ban-types', rule, {
- valid: [
- 'let f = Object();', // Should not fail if there is no options set
- 'let f: { x: number; y: number } = { x: 1, y: 1 };',
- {
- code: 'let f = Object();',
- options,
- },
- {
- code: 'let g = Object.create(null);',
- options,
- },
- {
- code: 'let h = String(false);',
- options,
- },
- {
- code: 'let e: foo.String;',
- options,
- },
- {
- code: 'let a: _.NS.Bad;',
- options,
- },
- {
- code: 'let a: NS.Bad._;',
- options,
- },
- // Replace default options instead of merging with extendDefaults: false
- {
- code: 'let a: String;',
- options: [
- {
- types: {
- Number: {
- message: 'Use number instead.',
- fixWith: 'number',
- },
- },
- extendDefaults: false,
- },
- ],
- },
- {
- code: 'let a: undefined;',
- options: [
- {
- types: {
- null: {
- message: 'Use undefined instead.',
- fixWith: 'undefined',
- },
- },
- },
- ],
- },
- {
- code: 'let a: null;',
- options: [
- {
- types: {
- undefined: null,
- },
- extendDefaults: false,
- },
- ],
- },
- {
- code: 'type Props = {};',
- options: [
- {
- types: {
- '{}': false,
- },
- extendDefaults: true,
- },
- ],
- },
- 'let a: [];',
- ],
- invalid: [
- {
- code: 'let a: String;',
- output: 'let a: string;',
- errors: [
- {
- messageId: 'bannedTypeMessage',
- line: 1,
- column: 8,
- },
- ],
- },
- {
- code: 'let a: Object;',
- output: null,
- errors: [
- {
- messageId: 'bannedTypeMessage',
- data: {
- name: 'Object',
- customMessage: " Use '{}' instead.",
- },
- line: 1,
- column: 8,
- },
- ],
- options,
- },
- {
- code: 'let aa: Foo;',
- output: null,
- errors: [
- {
- messageId: 'bannedTypeMessage',
- data: {
- name: 'Foo',
- customMessage: '',
- },
- },
- ],
- options: [
- {
- types: {
- Foo: { message: '' },
- },
- },
- ],
- },
- {
- code: 'let b: { c: String };',
- output: 'let b: { c: string };',
- errors: [
- {
- messageId: 'bannedTypeMessage',
- data: {
- name: 'String',
- customMessage: ' Use string instead.',
- },
- line: 1,
- column: 13,
- },
- ],
- options,
- },
- {
- code: 'function foo(a: String) {}',
- output: 'function foo(a: string) {}',
- errors: [
- {
- messageId: 'bannedTypeMessage',
- data: {
- name: 'String',
- customMessage: ' Use string instead.',
- },
- line: 1,
- column: 17,
- },
- ],
- options,
- },
- {
- code: "'a' as String;",
- output: "'a' as string;",
- errors: [
- {
- messageId: 'bannedTypeMessage',
- data: {
- name: 'String',
- customMessage: ' Use string instead.',
- },
- line: 1,
- column: 8,
- },
- ],
- options,
- },
- {
- code: 'let c: F;',
- output: null,
- errors: [
- {
- messageId: 'bannedTypeMessage',
- data: { name: 'F', customMessage: '' },
- line: 1,
- column: 8,
- },
- ],
- options,
- },
- {
- code: `
-class Foo extends Bar implements Baz