22 AST_NODE_TYPES ,
33 TSESTree ,
44} from '@typescript-eslint/experimental-utils' ;
5+ import * as tsutils from 'tsutils' ;
6+ import * as ts from 'typescript' ;
57import {
68 createRule ,
79 getParserServices ,
@@ -10,6 +12,13 @@ import {
1012 getWrappingFixer ,
1113} from '../util' ;
1214
15+ enum ArgumentType {
16+ Other = 0 ,
17+ String = 1 << 0 ,
18+ RegExp = 1 << 1 ,
19+ Both = String | RegExp ,
20+ }
21+
1322export default createRule ( {
1423 name : 'prefer-regexp-exec' ,
1524 defaultOptions : [ ] ,
@@ -37,25 +46,33 @@ export default createRule({
3746 const sourceCode = context . getSourceCode ( ) ;
3847
3948 /**
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.
4251 */
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' ;
4854 }
4955
5056 /**
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.
5359 */
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 ;
5976 }
6077
6178 return {
@@ -67,7 +84,13 @@ export default createRule({
6784 const argumentNode = callNode . arguments [ 0 ] ;
6885 const argumentValue = getStaticValue ( argumentNode , globalScope ) ;
6986
70- if ( ! isStringType ( objectNode ) ) {
87+ if (
88+ ! isStringType (
89+ typeChecker . getTypeAtLocation (
90+ parserServices . esTreeNodeToTSNodeMap . get ( objectNode ) ,
91+ ) ,
92+ )
93+ ) {
7194 return ;
7295 }
7396
@@ -97,38 +120,39 @@ export default createRule({
97120 } ) ;
98121 }
99122
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+ } ) ;
113142
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+ } ) ;
126155 }
127-
128- return context . report ( {
129- node : memberNode . property ,
130- messageId : 'regExpExecOverStringMatch' ,
131- } ) ;
132156 } ,
133157 } ;
134158 } ,
0 commit comments