2
2
AST_NODE_TYPES ,
3
3
TSESTree ,
4
4
} from '@typescript-eslint/experimental-utils' ;
5
+ import * as tsutils from 'tsutils' ;
6
+ import * as ts from 'typescript' ;
5
7
import {
6
8
createRule ,
7
9
getParserServices ,
@@ -10,6 +12,13 @@ import {
10
12
getWrappingFixer ,
11
13
} from '../util' ;
12
14
15
+ enum ArgumentType {
16
+ Other = 0 ,
17
+ String = 1 << 0 ,
18
+ RegExp = 1 << 1 ,
19
+ Both = String | RegExp ,
20
+ }
21
+
13
22
export default createRule ( {
14
23
name : 'prefer-regexp-exec' ,
15
24
defaultOptions : [ ] ,
@@ -37,25 +46,33 @@ export default createRule({
37
46
const sourceCode = context . getSourceCode ( ) ;
38
47
39
48
/**
40
- * Check if a given node is a string.
41
- * @param node The node to check.
49
+ * Check if a given node type is a string.
50
+ * @param node The node type to check.
42
51
*/
43
- function isStringType ( node : TSESTree . Node ) : boolean {
44
- const objectType = typeChecker . getTypeAtLocation (
45
- parserServices . esTreeNodeToTSNodeMap . get ( node ) ,
46
- ) ;
47
- return getTypeName ( typeChecker , objectType ) === 'string' ;
52
+ function isStringType ( type : ts . Type ) : boolean {
53
+ return getTypeName ( typeChecker , type ) === 'string' ;
48
54
}
49
55
50
56
/**
51
- * Check if a given node is a RegExp.
52
- * @param node The node to check.
57
+ * Check if a given node type is a RegExp.
58
+ * @param node The node type to check.
53
59
*/
54
- function isRegExpType ( node : TSESTree . Node ) : boolean {
55
- const objectType = typeChecker . getTypeAtLocation (
56
- parserServices . esTreeNodeToTSNodeMap . get ( node ) ,
57
- ) ;
58
- return getTypeName ( typeChecker , objectType ) === 'RegExp' ;
60
+ function isRegExpType ( type : ts . Type ) : boolean {
61
+ return getTypeName ( typeChecker , type ) === 'RegExp' ;
62
+ }
63
+
64
+ function collectArgumentTypes ( types : ts . Type [ ] ) : ArgumentType {
65
+ let result = ArgumentType . Other ;
66
+
67
+ for ( const type of types ) {
68
+ if ( isRegExpType ( type ) ) {
69
+ result |= ArgumentType . RegExp ;
70
+ } else if ( isStringType ( type ) ) {
71
+ result |= ArgumentType . String ;
72
+ }
73
+ }
74
+
75
+ return result ;
59
76
}
60
77
61
78
return {
@@ -67,7 +84,13 @@ export default createRule({
67
84
const argumentNode = callNode . arguments [ 0 ] ;
68
85
const argumentValue = getStaticValue ( argumentNode , globalScope ) ;
69
86
70
- if ( ! isStringType ( objectNode ) ) {
87
+ if (
88
+ ! isStringType (
89
+ typeChecker . getTypeAtLocation (
90
+ parserServices . esTreeNodeToTSNodeMap . get ( objectNode ) ,
91
+ ) ,
92
+ )
93
+ ) {
71
94
return ;
72
95
}
73
96
@@ -97,38 +120,39 @@ export default createRule({
97
120
} ) ;
98
121
}
99
122
100
- if ( isRegExpType ( argumentNode ) ) {
101
- return context . report ( {
102
- node : memberNode . property ,
103
- messageId : 'regExpExecOverStringMatch' ,
104
- fix : getWrappingFixer ( {
105
- sourceCode,
106
- node : callNode ,
107
- innerNode : [ objectNode , argumentNode ] ,
108
- wrap : ( objectCode , argumentCode ) =>
109
- `${ argumentCode } .exec(${ objectCode } )` ,
110
- } ) ,
111
- } ) ;
112
- }
123
+ const argumentType = typeChecker . getTypeAtLocation (
124
+ parserServices . esTreeNodeToTSNodeMap . get ( argumentNode ) ,
125
+ ) ;
126
+ const argumentTypes = collectArgumentTypes (
127
+ tsutils . unionTypeParts ( argumentType ) ,
128
+ ) ;
129
+ switch ( argumentTypes ) {
130
+ case ArgumentType . RegExp :
131
+ return context . report ( {
132
+ node : memberNode . property ,
133
+ messageId : 'regExpExecOverStringMatch' ,
134
+ fix : getWrappingFixer ( {
135
+ sourceCode,
136
+ node : callNode ,
137
+ innerNode : [ objectNode , argumentNode ] ,
138
+ wrap : ( objectCode , argumentCode ) =>
139
+ `${ argumentCode } .exec(${ objectCode } )` ,
140
+ } ) ,
141
+ } ) ;
113
142
114
- if ( isStringType ( argumentNode ) ) {
115
- return context . report ( {
116
- node : memberNode . property ,
117
- messageId : 'regExpExecOverStringMatch' ,
118
- fix : getWrappingFixer ( {
119
- sourceCode,
120
- node : callNode ,
121
- innerNode : [ objectNode , argumentNode ] ,
122
- wrap : ( objectCode , argumentCode ) =>
123
- `RegExp(${ argumentCode } ).exec(${ objectCode } )` ,
124
- } ) ,
125
- } ) ;
143
+ case ArgumentType . String :
144
+ return context . report ( {
145
+ node : memberNode . property ,
146
+ messageId : 'regExpExecOverStringMatch' ,
147
+ fix : getWrappingFixer ( {
148
+ sourceCode,
149
+ node : callNode ,
150
+ innerNode : [ objectNode , argumentNode ] ,
151
+ wrap : ( objectCode , argumentCode ) =>
152
+ `RegExp(${ argumentCode } ).exec(${ objectCode } )` ,
153
+ } ) ,
154
+ } ) ;
126
155
}
127
-
128
- return context . report ( {
129
- node : memberNode . property ,
130
- messageId : 'regExpExecOverStringMatch' ,
131
- } ) ;
132
156
} ,
133
157
} ;
134
158
} ,
0 commit comments