@@ -4,54 +4,86 @@ import ts from 'typescript';
44import { couldBeFunction , couldBeType , getTypeServices } from '../etc' ;
55import { ruleCreator } from '../utils' ;
66
7+ const defaultOptions : readonly {
8+ allowThrowingAny ?: boolean ;
9+ allowThrowingUnknown ?: boolean ;
10+ } [ ] = [ ] ;
11+
712export const throwErrorRule = ruleCreator ( {
8- defaultOptions : [ ] ,
13+ defaultOptions,
914 meta : {
1015 docs : {
1116 description :
12- 'Enforce passing only `Error` values to error notifications .' ,
17+ 'Enforce passing only `Error` values to `throwError` .' ,
1318 requiresTypeChecking : true ,
1419 } ,
1520 messages : {
16- forbidden : 'Passing non-Error values are forbidden.' ,
21+ forbidden : 'Passing non-Error values is forbidden.' ,
1722 } ,
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+ ] ,
1932 type : 'problem' ,
2033 } ,
2134 name : 'throw-error' ,
2235 create : ( context ) => {
2336 const { esTreeNodeToTSNodeMap, program, getTypeAtLocation } = ESLintUtils . getParserServices ( context ) ;
2437 const { couldBeObservable } = getTypeServices ( context ) ;
38+ const [ config = { } ] = context . options ;
39+ const { allowThrowingAny = true , allowThrowingUnknown = true } = config ;
2540
26- function checkNode ( node : es . Node ) {
41+ function checkThrowArgument ( node : es . Node ) {
2742 let type = getTypeAtLocation ( node ) ;
43+ let reportNode = node ;
44+
2845 if ( couldBeFunction ( type ) ) {
46+ reportNode = ( node as es . ArrowFunctionExpression ) . body ?? node ;
47+
2948 const tsNode = esTreeNodeToTSNodeMap . get ( node ) ;
3049 const annotation = ( tsNode as ts . ArrowFunction ) . type ;
3150 const body = ( tsNode as ts . ArrowFunction ) . body ;
3251 type = program . getTypeChecker ( ) . getTypeAtLocation ( annotation ?? body ) ;
3352 }
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+ }
4378 }
4479 }
4580
4681 return {
47- 'ThrowStatement > *' : checkNode ,
4882 '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 ) ;
5587 } ,
5688 } ;
5789 } ,
0 commit comments