diff --git a/docs/packages/type-utils/TypeOrValueSpecifier.mdx b/docs/packages/type-utils/TypeOrValueSpecifier.mdx index b9d76442406a..7a21d16cf26d 100644 --- a/docs/packages/type-utils/TypeOrValueSpecifier.mdx +++ b/docs/packages/type-utils/TypeOrValueSpecifier.mdx @@ -132,4 +132,5 @@ Universal string specifiers will be removed in a future major version of typescr - [`@typescript-eslint/no-floating-promises` > `allowForKnownSafeCalls`](/rules/no-floating-promises#allowforknownsafecalls) - [`@typescript-eslint/no-floating-promises` > `allowForKnownSafePromises`](/rules/no-floating-promises#allowforknownsafepromises) +- [`@typescript-eslint/only-throw-error` > `allow`](/rules/only-throw-error/#allow) - [`@typescript-eslint/prefer-readonly-parameter-types` > `allow`](/rules/prefer-readonly-parameter-types/#allow) diff --git a/packages/eslint-plugin/docs/rules/only-throw-error.mdx b/packages/eslint-plugin/docs/rules/only-throw-error.mdx index 8fffde428fee..109b1763ebf1 100644 --- a/packages/eslint-plugin/docs/rules/only-throw-error.mdx +++ b/packages/eslint-plugin/docs/rules/only-throw-error.mdx @@ -102,6 +102,27 @@ This rule adds the following options: ```ts interface Options { + /** + * Type specifiers that can be thrown. + */ + allow?: ( + | { + from: 'file'; + name: [string, ...string[]] | string; + path?: string; + } + | { + from: 'lib'; + name: [string, ...string[]] | string; + } + | { + from: 'package'; + name: [string, ...string[]] | string; + package: string; + } + | string + )[]; + /** * Whether to always allow throwing values typed as `any`. */ @@ -114,8 +135,9 @@ interface Options { } const defaultOptions: Options = { - allowThrowingAny: false, - allowThrowingUnknown: false, + allow: [], + allowThrowingAny: true, + allowThrowingUnknown: true, }; ``` diff --git a/packages/eslint-plugin/src/rules/only-throw-error.ts b/packages/eslint-plugin/src/rules/only-throw-error.ts index 0ebd6048bee3..87f6a1e34152 100644 --- a/packages/eslint-plugin/src/rules/only-throw-error.ts +++ b/packages/eslint-plugin/src/rules/only-throw-error.ts @@ -3,18 +3,23 @@ import type { TSESTree } from '@typescript-eslint/utils'; import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as ts from 'typescript'; +import type { TypeOrValueSpecifier } from '../util'; + import { createRule, getParserServices, isErrorLike, isTypeAnyType, isTypeUnknownType, + typeMatchesSomeSpecifier, + typeOrValueSpecifiersSchema, } from '../util'; type MessageIds = 'object' | 'undef'; type Options = [ { + allow?: TypeOrValueSpecifier[]; allowThrowingAny?: boolean; allowThrowingUnknown?: boolean; }, @@ -39,6 +44,10 @@ export default createRule({ type: 'object', additionalProperties: false, properties: { + allow: { + ...typeOrValueSpecifiersSchema, + description: 'Type specifiers that can be thrown.', + }, allowThrowingAny: { type: 'boolean', description: @@ -55,13 +64,14 @@ export default createRule({ }, defaultOptions: [ { + allow: [], allowThrowingAny: true, allowThrowingUnknown: true, }, ], create(context, [options]) { const services = getParserServices(context); - + const allow = options.allow; function checkThrowArgument(node: TSESTree.Node): void { if ( node.type === AST_NODE_TYPES.AwaitExpression || @@ -72,6 +82,10 @@ export default createRule({ const type = services.getTypeAtLocation(node); + if (typeMatchesSomeSpecifier(type, allow, services.program)) { + return; + } + if (type.flags & ts.TypeFlags.Undefined) { context.report({ node, messageId: 'undef' }); return; diff --git a/packages/eslint-plugin/tests/fixtures/errors.ts b/packages/eslint-plugin/tests/fixtures/errors.ts new file mode 100644 index 000000000000..b38f6885bf80 --- /dev/null +++ b/packages/eslint-plugin/tests/fixtures/errors.ts @@ -0,0 +1,6 @@ +// @ts-ignore +declare module 'errors' { + class ErrorLike {} + + export function createError(): ErrorLike; +} diff --git a/packages/eslint-plugin/tests/fixtures/tsconfig.json b/packages/eslint-plugin/tests/fixtures/tsconfig.json index a0fc993b1f48..d8141e1ddfa4 100644 --- a/packages/eslint-plugin/tests/fixtures/tsconfig.json +++ b/packages/eslint-plugin/tests/fixtures/tsconfig.json @@ -14,6 +14,7 @@ "deprecated.ts", "mixed-enums-decl.ts", "react.tsx", - "var-declaration.ts" + "var-declaration.ts", + "errors.ts" ] } diff --git a/packages/eslint-plugin/tests/rules/only-throw-error.test.ts b/packages/eslint-plugin/tests/rules/only-throw-error.test.ts index b20673db84ea..c7d52c6a1e1c 100644 --- a/packages/eslint-plugin/tests/rules/only-throw-error.test.ts +++ b/packages/eslint-plugin/tests/rules/only-throw-error.test.ts @@ -139,6 +139,56 @@ function fun(t: T): void { throw t; } `, + { + code: ` +throw undefined; + `, + options: [ + { + allow: [{ from: 'lib', name: 'undefined' }], + allowThrowingAny: false, + allowThrowingUnknown: false, + }, + ], + }, + { + code: ` +class CustomError implements Error {} +throw new CustomError(); + `, + options: [ + { + allow: [{ from: 'file', name: 'CustomError' }], + allowThrowingAny: false, + allowThrowingUnknown: false, + }, + ], + }, + { + code: ` +throw new Map(); + `, + options: [ + { + allow: [{ from: 'lib', name: 'Map' }], + allowThrowingAny: false, + allowThrowingUnknown: false, + }, + ], + }, + { + code: ` + import { createError } from 'errors'; + throw createError(); + `, + options: [ + { + allow: [{ from: 'package', name: 'ErrorLike', package: 'errors' }], + allowThrowingAny: false, + allowThrowingUnknown: false, + }, + ], + }, ], invalid: [ { @@ -485,5 +535,23 @@ function fun(t: T): void { }, ], }, + { + code: ` +class UnknownError implements Error {} +throw new UnknownError(); + `, + errors: [ + { + messageId: 'object', + }, + ], + options: [ + { + allow: [{ from: 'file', name: 'CustomError' }], + allowThrowingAny: false, + allowThrowingUnknown: false, + }, + ], + }, ], }); diff --git a/packages/eslint-plugin/tests/schema-snapshots/only-throw-error.shot b/packages/eslint-plugin/tests/schema-snapshots/only-throw-error.shot index 05b6ae4af6b0..c2ddc66bcc81 100644 --- a/packages/eslint-plugin/tests/schema-snapshots/only-throw-error.shot +++ b/packages/eslint-plugin/tests/schema-snapshots/only-throw-error.shot @@ -8,6 +8,101 @@ exports[`Rule schemas should be convertible to TS types for documentation purpos { "additionalProperties": false, "properties": { + "allow": { + "description": "Type specifiers that can be thrown.", + "items": { + "oneOf": [ + { + "type": "string" + }, + { + "additionalProperties": false, + "properties": { + "from": { + "enum": ["file"], + "type": "string" + }, + "name": { + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "minItems": 1, + "type": "array", + "uniqueItems": true + } + ] + }, + "path": { + "type": "string" + } + }, + "required": ["from", "name"], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "from": { + "enum": ["lib"], + "type": "string" + }, + "name": { + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "minItems": 1, + "type": "array", + "uniqueItems": true + } + ] + } + }, + "required": ["from", "name"], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "from": { + "enum": ["package"], + "type": "string" + }, + "name": { + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "minItems": 1, + "type": "array", + "uniqueItems": true + } + ] + }, + "package": { + "type": "string" + } + }, + "required": ["from", "name", "package"], + "type": "object" + } + ] + }, + "type": "array" + }, "allowThrowingAny": { "description": "Whether to always allow throwing values typed as \`any\`.", "type": "boolean" @@ -26,6 +121,24 @@ exports[`Rule schemas should be convertible to TS types for documentation purpos type Options = [ { + /** Type specifiers that can be thrown. */ + allow?: ( + | { + from: 'file'; + name: [string, ...string[]] | string; + path?: string; + } + | { + from: 'lib'; + name: [string, ...string[]] | string; + } + | { + from: 'package'; + name: [string, ...string[]] | string; + package: string; + } + | string + )[]; /** Whether to always allow throwing values typed as \`any\`. */ allowThrowingAny?: boolean; /** Whether to always allow throwing values typed as \`unknown\`. */ pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy