Skip to content

Commit c1fe3ea

Browse files
feat(eslint-plugin): [only-throw-error] add allow option (typescript-eslint#10221)
* feat(eslint-plugin): [only-throw-error] add allowThrowing option * ts ignore * Update only-throw-error.shot * review * Add to TypeOrValueSpecifier.mdx options --------- Co-authored-by: Josh Goldberg <git@joshuakgoldberg.com>
1 parent 3664c20 commit c1fe3ea

File tree

7 files changed

+229
-4
lines changed

7 files changed

+229
-4
lines changed

docs/packages/type-utils/TypeOrValueSpecifier.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,4 +132,5 @@ Universal string specifiers will be removed in a future major version of typescr
132132

133133
- [`@typescript-eslint/no-floating-promises` > `allowForKnownSafeCalls`](/rules/no-floating-promises#allowforknownsafecalls)
134134
- [`@typescript-eslint/no-floating-promises` > `allowForKnownSafePromises`](/rules/no-floating-promises#allowforknownsafepromises)
135+
- [`@typescript-eslint/only-throw-error` > `allow`](/rules/only-throw-error/#allow)
135136
- [`@typescript-eslint/prefer-readonly-parameter-types` > `allow`](/rules/prefer-readonly-parameter-types/#allow)

packages/eslint-plugin/docs/rules/only-throw-error.mdx

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,27 @@ This rule adds the following options:
102102

103103
```ts
104104
interface Options {
105+
/**
106+
* Type specifiers that can be thrown.
107+
*/
108+
allow?: (
109+
| {
110+
from: 'file';
111+
name: [string, ...string[]] | string;
112+
path?: string;
113+
}
114+
| {
115+
from: 'lib';
116+
name: [string, ...string[]] | string;
117+
}
118+
| {
119+
from: 'package';
120+
name: [string, ...string[]] | string;
121+
package: string;
122+
}
123+
| string
124+
)[];
125+
105126
/**
106127
* Whether to always allow throwing values typed as `any`.
107128
*/
@@ -114,8 +135,9 @@ interface Options {
114135
}
115136

116137
const defaultOptions: Options = {
117-
allowThrowingAny: false,
118-
allowThrowingUnknown: false,
138+
allow: [],
139+
allowThrowingAny: true,
140+
allowThrowingUnknown: true,
119141
};
120142
```
121143

packages/eslint-plugin/src/rules/only-throw-error.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,23 @@ import type { TSESTree } from '@typescript-eslint/utils';
33
import { AST_NODE_TYPES } from '@typescript-eslint/utils';
44
import * as ts from 'typescript';
55

6+
import type { TypeOrValueSpecifier } from '../util';
7+
68
import {
79
createRule,
810
getParserServices,
911
isErrorLike,
1012
isTypeAnyType,
1113
isTypeUnknownType,
14+
typeMatchesSomeSpecifier,
15+
typeOrValueSpecifiersSchema,
1216
} from '../util';
1317

1418
type MessageIds = 'object' | 'undef';
1519

1620
type Options = [
1721
{
22+
allow?: TypeOrValueSpecifier[];
1823
allowThrowingAny?: boolean;
1924
allowThrowingUnknown?: boolean;
2025
},
@@ -39,6 +44,10 @@ export default createRule<Options, MessageIds>({
3944
type: 'object',
4045
additionalProperties: false,
4146
properties: {
47+
allow: {
48+
...typeOrValueSpecifiersSchema,
49+
description: 'Type specifiers that can be thrown.',
50+
},
4251
allowThrowingAny: {
4352
type: 'boolean',
4453
description:
@@ -55,13 +64,14 @@ export default createRule<Options, MessageIds>({
5564
},
5665
defaultOptions: [
5766
{
67+
allow: [],
5868
allowThrowingAny: true,
5969
allowThrowingUnknown: true,
6070
},
6171
],
6272
create(context, [options]) {
6373
const services = getParserServices(context);
64-
74+
const allow = options.allow;
6575
function checkThrowArgument(node: TSESTree.Node): void {
6676
if (
6777
node.type === AST_NODE_TYPES.AwaitExpression ||
@@ -72,6 +82,10 @@ export default createRule<Options, MessageIds>({
7282

7383
const type = services.getTypeAtLocation(node);
7484

85+
if (typeMatchesSomeSpecifier(type, allow, services.program)) {
86+
return;
87+
}
88+
7589
if (type.flags & ts.TypeFlags.Undefined) {
7690
context.report({ node, messageId: 'undef' });
7791
return;
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// @ts-ignore
2+
declare module 'errors' {
3+
class ErrorLike {}
4+
5+
export function createError(): ErrorLike;
6+
}

packages/eslint-plugin/tests/fixtures/tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"deprecated.ts",
1515
"mixed-enums-decl.ts",
1616
"react.tsx",
17-
"var-declaration.ts"
17+
"var-declaration.ts",
18+
"errors.ts"
1819
]
1920
}

packages/eslint-plugin/tests/rules/only-throw-error.test.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,56 @@ function fun<T extends Error>(t: T): void {
139139
throw t;
140140
}
141141
`,
142+
{
143+
code: `
144+
throw undefined;
145+
`,
146+
options: [
147+
{
148+
allow: [{ from: 'lib', name: 'undefined' }],
149+
allowThrowingAny: false,
150+
allowThrowingUnknown: false,
151+
},
152+
],
153+
},
154+
{
155+
code: `
156+
class CustomError implements Error {}
157+
throw new CustomError();
158+
`,
159+
options: [
160+
{
161+
allow: [{ from: 'file', name: 'CustomError' }],
162+
allowThrowingAny: false,
163+
allowThrowingUnknown: false,
164+
},
165+
],
166+
},
167+
{
168+
code: `
169+
throw new Map();
170+
`,
171+
options: [
172+
{
173+
allow: [{ from: 'lib', name: 'Map' }],
174+
allowThrowingAny: false,
175+
allowThrowingUnknown: false,
176+
},
177+
],
178+
},
179+
{
180+
code: `
181+
import { createError } from 'errors';
182+
throw createError();
183+
`,
184+
options: [
185+
{
186+
allow: [{ from: 'package', name: 'ErrorLike', package: 'errors' }],
187+
allowThrowingAny: false,
188+
allowThrowingUnknown: false,
189+
},
190+
],
191+
},
142192
],
143193
invalid: [
144194
{
@@ -485,5 +535,23 @@ function fun<T extends number>(t: T): void {
485535
},
486536
],
487537
},
538+
{
539+
code: `
540+
class UnknownError implements Error {}
541+
throw new UnknownError();
542+
`,
543+
errors: [
544+
{
545+
messageId: 'object',
546+
},
547+
],
548+
options: [
549+
{
550+
allow: [{ from: 'file', name: 'CustomError' }],
551+
allowThrowingAny: false,
552+
allowThrowingUnknown: false,
553+
},
554+
],
555+
},
488556
],
489557
});

packages/eslint-plugin/tests/schema-snapshots/only-throw-error.shot

Lines changed: 113 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)
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