@@ -4,54 +4,86 @@ import ts from 'typescript';
4
4
import { couldBeFunction , couldBeType , getTypeServices } from '../etc' ;
5
5
import { ruleCreator } from '../utils' ;
6
6
7
+ const defaultOptions : readonly {
8
+ allowThrowingAny ?: boolean ;
9
+ allowThrowingUnknown ?: boolean ;
10
+ } [ ] = [ ] ;
11
+
7
12
export const throwErrorRule = ruleCreator ( {
8
- defaultOptions : [ ] ,
13
+ defaultOptions,
9
14
meta : {
10
15
docs : {
11
16
description :
12
- 'Enforce passing only `Error` values to error notifications .' ,
17
+ 'Enforce passing only `Error` values to `throwError` .' ,
13
18
requiresTypeChecking : true ,
14
19
} ,
15
20
messages : {
16
- forbidden : 'Passing non-Error values are forbidden.' ,
21
+ forbidden : 'Passing non-Error values is forbidden.' ,
17
22
} ,
18
- schema : [ ] ,
23
+ schema : [
24
+ {
25
+ properties : {
26
+ allowThrowingAny : { type : 'boolean' , default : true , description : 'Whether to always allow throwing values typed as `any`.' } ,
27
+ allowThrowingUnknown : { type : 'boolean' , default : true , description : 'Whether to always allow throwing values typed as `unknown`.' } ,
28
+ } ,
29
+ type : 'object' ,
30
+ } ,
31
+ ] ,
19
32
type : 'problem' ,
20
33
} ,
21
34
name : 'throw-error' ,
22
35
create : ( context ) => {
23
36
const { esTreeNodeToTSNodeMap, program, getTypeAtLocation } = ESLintUtils . getParserServices ( context ) ;
24
37
const { couldBeObservable } = getTypeServices ( context ) ;
38
+ const [ config = { } ] = context . options ;
39
+ const { allowThrowingAny = true , allowThrowingUnknown = true } = config ;
25
40
26
- function checkNode ( node : es . Node ) {
41
+ function checkThrowArgument ( node : es . Node ) {
27
42
let type = getTypeAtLocation ( node ) ;
43
+ let reportNode = node ;
44
+
28
45
if ( couldBeFunction ( type ) ) {
46
+ reportNode = ( node as es . ArrowFunctionExpression ) . body ?? node ;
47
+
29
48
const tsNode = esTreeNodeToTSNodeMap . get ( node ) ;
30
49
const annotation = ( tsNode as ts . ArrowFunction ) . type ;
31
50
const body = ( tsNode as ts . ArrowFunction ) . body ;
32
51
type = program . getTypeChecker ( ) . getTypeAtLocation ( annotation ?? body ) ;
33
52
}
34
- if (
35
- ! tsutils . isIntrinsicAnyType ( type )
36
- && ! tsutils . isIntrinsicUnknownType ( type )
37
- && ! couldBeType ( type , / ^ ( E r r o r | D O M E x c e p t i o n ) $ / )
38
- ) {
39
- context . report ( {
40
- messageId : 'forbidden' ,
41
- node,
42
- } ) ;
53
+
54
+ if ( allowThrowingAny && tsutils . isIntrinsicAnyType ( type ) ) {
55
+ return ;
56
+ }
57
+
58
+ if ( allowThrowingUnknown && tsutils . isIntrinsicUnknownType ( type ) ) {
59
+ return ;
60
+ }
61
+
62
+ if ( couldBeType ( type , / ^ E r r o r $ / ) ) {
63
+ return ;
64
+ }
65
+
66
+ context . report ( {
67
+ messageId : 'forbidden' ,
68
+ node : reportNode ,
69
+ } ) ;
70
+ }
71
+
72
+ function checkNode ( node : es . CallExpression ) {
73
+ if ( couldBeObservable ( node ) ) {
74
+ const [ arg ] = node . arguments ;
75
+ if ( arg ) {
76
+ checkThrowArgument ( arg ) ;
77
+ }
43
78
}
44
79
}
45
80
46
81
return {
47
- 'ThrowStatement > *' : checkNode ,
48
82
'CallExpression[callee.name=\'throwError\']' : ( node : es . CallExpression ) => {
49
- if ( couldBeObservable ( node ) ) {
50
- const [ arg ] = node . arguments ;
51
- if ( arg ) {
52
- checkNode ( arg ) ;
53
- }
54
- }
83
+ checkNode ( node ) ;
84
+ } ,
85
+ 'CallExpression[callee.property.name=\'throwError\']' : ( node : es . CallExpression ) => {
86
+ checkNode ( node ) ;
55
87
} ,
56
88
} ;
57
89
} ,
0 commit comments