@@ -10,7 +10,11 @@ export type Options = [
10
10
ignoreRestArgs ?: boolean ;
11
11
} ,
12
12
] ;
13
- export type MessageIds = 'suggestNever' | 'suggestUnknown' | 'unexpectedAny' ;
13
+ export type MessageIds =
14
+ | 'suggestNever'
15
+ | 'suggestPropertyKey'
16
+ | 'suggestUnknown'
17
+ | 'unexpectedAny' ;
14
18
15
19
export default createRule < Options , MessageIds > ( {
16
20
name : 'no-explicit-any' ,
@@ -25,6 +29,8 @@ export default createRule<Options, MessageIds>({
25
29
messages : {
26
30
suggestNever :
27
31
"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of." ,
32
+ suggestPropertyKey :
33
+ 'Use `PropertyKey` instead, this is more explicit than `keyof any`.' ,
28
34
suggestUnknown :
29
35
'Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct.' ,
30
36
unexpectedAny : 'Unexpected any. Specify a different type.' ,
@@ -170,8 +176,35 @@ export default createRule<Options, MessageIds>({
170
176
) ;
171
177
}
172
178
179
+ /**
180
+ * Checks if the node is within a keyof any expression
181
+ * @param node the node to be validated.
182
+ * @returns true if the node is within a keyof any expression, false otherwise
183
+ * @private
184
+ */
185
+ function isNodeWithinKeyofAny ( node : TSESTree . TSAnyKeyword ) : boolean {
186
+ return (
187
+ node . parent . type === AST_NODE_TYPES . TSTypeOperator &&
188
+ node . parent . operator === 'keyof'
189
+ ) ;
190
+ }
191
+
192
+ /**
193
+ * Creates a fixer that replaces a keyof any with PropertyKey
194
+ * @param node the node to be fixed.
195
+ * @returns a function that will fix the node.
196
+ * @private
197
+ */
198
+ function createPropertyKeyFixer ( node : TSESTree . TSAnyKeyword ) {
199
+ return ( fixer : TSESLint . RuleFixer ) => {
200
+ return fixer . replaceText ( node . parent , 'PropertyKey' ) ;
201
+ } ;
202
+ }
203
+
173
204
return {
174
205
TSAnyKeyword ( node ) : void {
206
+ const isKeyofAny = isNodeWithinKeyofAny ( node ) ;
207
+
175
208
if ( ignoreRestArgs && isNodeDescendantOfRestElementInFunction ( node ) ) {
176
209
return ;
177
210
}
@@ -181,25 +214,29 @@ export default createRule<Options, MessageIds>({
181
214
suggest : TSESLint . ReportSuggestionArray < MessageIds > | null ;
182
215
} = {
183
216
fix : null ,
184
- suggest : [
185
- {
186
- messageId : 'suggestUnknown' ,
187
- fix ( fixer ) : TSESLint . RuleFix {
188
- return fixer . replaceText ( node , 'unknown' ) ;
189
- } ,
190
- } ,
191
- {
192
- messageId : 'suggestNever' ,
193
- fix ( fixer ) : TSESLint . RuleFix {
194
- return fixer . replaceText ( node , 'never' ) ;
195
- } ,
196
- } ,
197
- ] ,
217
+ suggest : isKeyofAny
218
+ ? [
219
+ {
220
+ messageId : 'suggestPropertyKey' ,
221
+ fix : createPropertyKeyFixer ( node ) ,
222
+ } ,
223
+ ]
224
+ : [
225
+ {
226
+ messageId : 'suggestUnknown' ,
227
+ fix : fixer => fixer . replaceText ( node , 'unknown' ) ,
228
+ } ,
229
+ {
230
+ messageId : 'suggestNever' ,
231
+ fix : fixer => fixer . replaceText ( node , 'never' ) ,
232
+ } ,
233
+ ] ,
198
234
} ;
199
235
200
236
if ( fixToUnknown ) {
201
- fixOrSuggest . fix = ( fixer ) : TSESLint . RuleFix =>
202
- fixer . replaceText ( node , 'unknown' ) ;
237
+ fixOrSuggest . fix = isKeyofAny
238
+ ? createPropertyKeyFixer ( node )
239
+ : fixer => fixer . replaceText ( node , 'unknown' ) ;
203
240
}
204
241
205
242
context . report ( {
0 commit comments