@foundbyte/eslint-plugin 1.1.4-alpha.2 → 1.1.4-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/lib/rules/no-excessive-any.js +229 -123
- package/lib/utils/is-built-in-function.js +78 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ module.exports = {
|
|
|
25
25
|
'@foundbyte/arrow-function-padding': 'error',
|
|
26
26
|
'@foundbyte/no-initial-as-assertion': ['error', {
|
|
27
27
|
excludeVariableAssertions: true,
|
|
28
|
-
excludeTypePatterns: ['^const$', '^[A-Z]$'],
|
|
28
|
+
excludeTypePatterns: ['^const$', '^[A-Z]$', '^unknown$'],
|
|
29
29
|
excludeMethodReturns: ['parse', 'querySelector']
|
|
30
30
|
}],
|
|
31
31
|
'@foundbyte/hooks-arrow-function-only': 'error',
|
|
@@ -26,11 +26,16 @@ module.exports = {
|
|
|
26
26
|
},
|
|
27
27
|
|
|
28
28
|
create(context) {
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
const options = context.options[0] || {}
|
|
30
|
+
const maxAnyCount = options.maxAnyCount || 0
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
// 导入共享工具函数
|
|
33
|
+
const { isBuiltInFunction, isSimpleCollectionInstantiation } = require('../utils/is-built-in-function')
|
|
34
|
+
|
|
35
|
+
// 存储 any 类型使用的位置
|
|
36
|
+
const anyUsages = []
|
|
37
|
+
// 存储 any 类型的变量名
|
|
38
|
+
const anyVariables = new Set()
|
|
34
39
|
|
|
35
40
|
/**
|
|
36
41
|
* 检查文件路径是否应该被排除
|
|
@@ -62,6 +67,13 @@ module.exports = {
|
|
|
62
67
|
})
|
|
63
68
|
}
|
|
64
69
|
|
|
70
|
+
// 检查类型参数实例化(如 ref<any[]> 中的 <any[]>)
|
|
71
|
+
if (node.type === 'TSTypeParameterInstantiation' && node.params) {
|
|
72
|
+
node.params.forEach(param => {
|
|
73
|
+
checkTypeNodeForAny(param, parentNode || node)
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
|
|
65
77
|
// 检查联合类型和交叉类型
|
|
66
78
|
if ((node.type === 'TSUnionType' || node.type === 'TSIntersectionType') && node.types) {
|
|
67
79
|
node.types.forEach(type => {
|
|
@@ -85,6 +97,32 @@ module.exports = {
|
|
|
85
97
|
checkTypeNodeForAny(node.returnType.typeAnnotation, node)
|
|
86
98
|
}
|
|
87
99
|
}
|
|
100
|
+
|
|
101
|
+
// 检查元组类型
|
|
102
|
+
if (node.type === 'TSTupleType' && node.elementTypes) {
|
|
103
|
+
node.elementTypes.forEach(elementType => {
|
|
104
|
+
checkTypeNodeForAny(elementType, node)
|
|
105
|
+
})
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// 检查条件类型
|
|
109
|
+
if (node.type === 'TSConditionalType') {
|
|
110
|
+
checkTypeNodeForAny(node.checkType, node)
|
|
111
|
+
checkTypeNodeForAny(node.extendsType, node)
|
|
112
|
+
checkTypeNodeForAny(node.trueType, node)
|
|
113
|
+
checkTypeNodeForAny(node.falseType, node)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// 检查索引访问类型
|
|
117
|
+
if (node.type === 'TSIndexedAccessType') {
|
|
118
|
+
checkTypeNodeForAny(node.objectType, node)
|
|
119
|
+
checkTypeNodeForAny(node.indexType, node)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// 检查映射类型
|
|
123
|
+
if (node.type === 'TSMappedType' && node.typeAnnotation) {
|
|
124
|
+
checkTypeNodeForAny(node.typeAnnotation, node)
|
|
125
|
+
}
|
|
88
126
|
}
|
|
89
127
|
|
|
90
128
|
/**
|
|
@@ -94,58 +132,18 @@ module.exports = {
|
|
|
94
132
|
if (shouldExcludeFile()) return
|
|
95
133
|
if (node.typeAnnotation && node.typeAnnotation.typeAnnotation) {
|
|
96
134
|
checkTypeNodeForAny(node.typeAnnotation.typeAnnotation, node)
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
function isBuiltInFunction(node) {
|
|
104
|
-
if (node.type !== 'CallExpression') return false
|
|
105
|
-
|
|
106
|
-
// 检查全局对象的方法调用,如 JSON.parse, Promise.all 等
|
|
107
|
-
if (node.callee.type === 'MemberExpression' && node.callee.object.type === 'Identifier') {
|
|
108
|
-
const objectName = node.callee.object.name
|
|
109
|
-
|
|
110
|
-
// 内置全局对象列表 - 这些对象的所有方法都认为是内置的
|
|
111
|
-
const builtInObjects = ['JSON', 'Promise', 'Array', 'Object', 'String', 'Number', 'Boolean', 'Date', 'Math', 'RegExp', 'Map', 'Set', 'WeakMap', 'WeakSet']
|
|
112
|
-
|
|
113
|
-
if (builtInObjects.includes(objectName)) {
|
|
114
|
-
// 对于这些内置对象,假设它们的所有方法都是内置的
|
|
115
|
-
// 这样可以避免维护庞大的方法列表
|
|
116
|
-
return true
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// 检查可选链调用,如 spotArr?.find()
|
|
121
|
-
if (node.callee.type === 'ChainExpression' && node.callee.expression.type === 'CallExpression') {
|
|
122
|
-
const chainCall = node.callee.expression
|
|
123
|
-
if (chainCall.callee.type === 'MemberExpression' && chainCall.callee.object.type === 'Identifier') {
|
|
124
|
-
const objectName = chainCall.callee.object.name
|
|
125
|
-
|
|
126
|
-
// 内置全局对象列表
|
|
127
|
-
const builtInObjects = ['JSON', 'Promise', 'Array', 'Object', 'String', 'Number', 'Boolean', 'Date', 'Math', 'RegExp', 'Map', 'Set', 'WeakMap', 'WeakSet']
|
|
128
|
-
|
|
129
|
-
if (builtInObjects.includes(objectName)) {
|
|
130
|
-
// 对于这些内置对象,假设它们的所有方法都是内置的
|
|
131
|
-
return true
|
|
135
|
+
// 如果节点是标识符且有名称,检查类型注解中是否有 any
|
|
136
|
+
if (node.type === 'Identifier' && node.name) {
|
|
137
|
+
const typeAnnotation = node.typeAnnotation.typeAnnotation
|
|
138
|
+
if (typeAnnotation && typeAnnotation.type === 'TSAnyKeyword') {
|
|
139
|
+
// 记录 any 类型的变量名
|
|
140
|
+
anyVariables.add(node.name)
|
|
132
141
|
}
|
|
133
142
|
}
|
|
134
143
|
}
|
|
135
|
-
|
|
136
|
-
// 检查全局函数调用,如 parseInt, parseFloat, isNaN 等
|
|
137
|
-
if (node.callee.type === 'Identifier') {
|
|
138
|
-
const functionName = node.callee.name
|
|
139
|
-
const globalFunctions = [
|
|
140
|
-
'parseInt', 'parseFloat', 'isNaN', 'isFinite', 'eval', 'encodeURI',
|
|
141
|
-
'encodeURIComponent', 'decodeURI', 'decodeURIComponent', 'escape', 'unescape'
|
|
142
|
-
]
|
|
143
|
-
return globalFunctions.includes(functionName)
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
return false
|
|
147
144
|
}
|
|
148
145
|
|
|
146
|
+
|
|
149
147
|
/**
|
|
150
148
|
* 检查表达式是否可以推导类型
|
|
151
149
|
*/
|
|
@@ -172,6 +170,21 @@ module.exports = {
|
|
|
172
170
|
return true
|
|
173
171
|
}
|
|
174
172
|
|
|
173
|
+
// 二元表达式可以推导类型(如 1 + 1)
|
|
174
|
+
if (node.type === 'BinaryExpression') {
|
|
175
|
+
return true
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// 一元表达式可以推导类型(如 -1, !true)
|
|
179
|
+
if (node.type === 'UnaryExpression') {
|
|
180
|
+
return true
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// 标识符通常可以推导类型(变量有类型注解)
|
|
184
|
+
if (node.type === 'Identifier') {
|
|
185
|
+
return true
|
|
186
|
+
}
|
|
187
|
+
|
|
175
188
|
// 函数调用:检查参数是否可以推导类型
|
|
176
189
|
if (node.type === 'CallExpression') {
|
|
177
190
|
// 检查是否是第三方库函数调用
|
|
@@ -231,6 +244,15 @@ module.exports = {
|
|
|
231
244
|
return true
|
|
232
245
|
}
|
|
233
246
|
|
|
247
|
+
// 条件表达式(三元表达式)可以推导类型
|
|
248
|
+
if (node.type === 'ConditionalExpression') {
|
|
249
|
+
// 检查条件和两个分支是否可以推导类型
|
|
250
|
+
const testCanInfer = canInferTypeFromExpression(node.test)
|
|
251
|
+
const consequentCanInfer = canInferTypeFromExpression(node.consequent)
|
|
252
|
+
const alternateCanInfer = canInferTypeFromExpression(node.alternate)
|
|
253
|
+
return testCanInfer && consequentCanInfer && alternateCanInfer
|
|
254
|
+
}
|
|
255
|
+
|
|
234
256
|
// 其他情况默认无法推导类型
|
|
235
257
|
return false
|
|
236
258
|
}
|
|
@@ -274,55 +296,6 @@ module.exports = {
|
|
|
274
296
|
return false
|
|
275
297
|
}
|
|
276
298
|
|
|
277
|
-
/**
|
|
278
|
-
* 检查是否是内置函数
|
|
279
|
-
*/
|
|
280
|
-
function isBuiltInFunction(node) {
|
|
281
|
-
if (node.type !== 'CallExpression') return false
|
|
282
|
-
|
|
283
|
-
// 检查全局对象的方法调用,如 JSON.parse, Promise.all 等
|
|
284
|
-
if (node.callee.type === 'MemberExpression' && node.callee.object.type === 'Identifier') {
|
|
285
|
-
const objectName = node.callee.object.name
|
|
286
|
-
const propertyName = node.callee.property.type === 'Identifier' ? node.callee.property.name : ''
|
|
287
|
-
|
|
288
|
-
// 内置全局对象列表
|
|
289
|
-
const builtInObjects = ['JSON', 'Promise', 'Array', 'Object', 'String', 'Number', 'Boolean', 'Date', 'Math', 'RegExp', 'Map', 'Set', 'WeakMap', 'WeakSet']
|
|
290
|
-
|
|
291
|
-
if (builtInObjects.includes(objectName)) {
|
|
292
|
-
// 对于这些内置对象,假设它们的所有方法都是内置的
|
|
293
|
-
// 这样可以避免维护庞大的方法列表
|
|
294
|
-
return true
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
// 检查可选链调用,如 spotArr?.find()
|
|
299
|
-
if (node.callee.type === 'ChainExpression' && node.callee.expression.type === 'CallExpression') {
|
|
300
|
-
const chainCall = node.callee.expression
|
|
301
|
-
if (chainCall.callee.type === 'MemberExpression' && chainCall.callee.object.type === 'Identifier') {
|
|
302
|
-
const objectName = chainCall.callee.object.name
|
|
303
|
-
|
|
304
|
-
// 内置全局对象列表
|
|
305
|
-
const builtInObjects = ['JSON', 'Promise', 'Array', 'Object', 'String', 'Number', 'Boolean', 'Date', 'Math', 'RegExp', 'Map', 'Set', 'WeakMap', 'WeakSet']
|
|
306
|
-
|
|
307
|
-
if (builtInObjects.includes(objectName)) {
|
|
308
|
-
// 对于这些内置对象,假设它们的所有方法都是内置的
|
|
309
|
-
return true
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
// 检查全局函数调用,如 parseInt, parseFloat, isNaN 等
|
|
315
|
-
if (node.callee.type === 'Identifier') {
|
|
316
|
-
const functionName = node.callee.name
|
|
317
|
-
const globalFunctions = [
|
|
318
|
-
'parseInt', 'parseFloat', 'isNaN', 'isFinite', 'eval', 'encodeURI',
|
|
319
|
-
'encodeURIComponent', 'decodeURI', 'decodeURIComponent', 'escape', 'unescape'
|
|
320
|
-
]
|
|
321
|
-
return globalFunctions.includes(functionName)
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
return false
|
|
325
|
-
}
|
|
326
299
|
|
|
327
300
|
/**
|
|
328
301
|
* 检查模块路径是否应该被排除
|
|
@@ -421,6 +394,10 @@ module.exports = {
|
|
|
421
394
|
})
|
|
422
395
|
}
|
|
423
396
|
}
|
|
397
|
+
} else {
|
|
398
|
+
// 如果可以推导类型,还需要检查初始值中是否包含显式 any
|
|
399
|
+
// 例如:ref<any[]>() 中的 any[] 包含 any
|
|
400
|
+
checkExpressionForAny(declaration.init)
|
|
424
401
|
}
|
|
425
402
|
}
|
|
426
403
|
}
|
|
@@ -472,26 +449,6 @@ module.exports = {
|
|
|
472
449
|
return false
|
|
473
450
|
}
|
|
474
451
|
|
|
475
|
-
/**
|
|
476
|
-
* 检查是否是简单的集合实例化(如 new Map(), new Set())
|
|
477
|
-
* 这些可以通过上下文推导类型,不应该算作隐式 any
|
|
478
|
-
*/
|
|
479
|
-
function isSimpleCollectionInstantiation(node) {
|
|
480
|
-
if (!node) return false
|
|
481
|
-
|
|
482
|
-
// 检查 new Map() 或 new Set()
|
|
483
|
-
if (node.type === 'NewExpression') {
|
|
484
|
-
const calleeName = node.callee.type === 'Identifier' ? node.callee.name : ''
|
|
485
|
-
if (calleeName === 'Map' || calleeName === 'Set') {
|
|
486
|
-
// 检查是否有参数(无参数的 new Map() 或 new Set() 是简单的实例化)
|
|
487
|
-
if (!node.arguments || node.arguments.length === 0) {
|
|
488
|
-
return true
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
return false
|
|
494
|
-
}
|
|
495
452
|
|
|
496
453
|
/**
|
|
497
454
|
* 检查函数声明中的 any 类型
|
|
@@ -533,6 +490,155 @@ module.exports = {
|
|
|
533
490
|
}
|
|
534
491
|
})
|
|
535
492
|
}
|
|
493
|
+
|
|
494
|
+
// 检查箭头函数体中的 any 类型变量使用
|
|
495
|
+
if (node.body) {
|
|
496
|
+
if (node.body.type === 'BlockStatement') {
|
|
497
|
+
// 遍历函数体中的语句
|
|
498
|
+
node.body.body.forEach(statement => {
|
|
499
|
+
// 检查返回语句
|
|
500
|
+
if (statement.type === 'ReturnStatement' && statement.argument) {
|
|
501
|
+
checkExpressionForAnyVariableUse(statement.argument)
|
|
502
|
+
}
|
|
503
|
+
// 检查表达式语句
|
|
504
|
+
else if (statement.type === 'ExpressionStatement') {
|
|
505
|
+
checkExpressionForAnyVariableUse(statement.expression)
|
|
506
|
+
}
|
|
507
|
+
})
|
|
508
|
+
} else {
|
|
509
|
+
// 简写箭头函数体(直接返回表达式)
|
|
510
|
+
checkExpressionForAnyVariableUse(node.body)
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* 检查表达式中的 any 类型
|
|
517
|
+
*/
|
|
518
|
+
function checkExpressionForAny(node) {
|
|
519
|
+
if (!node) return
|
|
520
|
+
|
|
521
|
+
// 检查函数调用中的类型参数
|
|
522
|
+
if (node.type === 'CallExpression') {
|
|
523
|
+
const sourceCode = context.getSourceCode()
|
|
524
|
+
const callText = sourceCode.getText(node)
|
|
525
|
+
|
|
526
|
+
// 检查类型参数(泛型)
|
|
527
|
+
if (node.typeParameters) {
|
|
528
|
+
// 直接检查类型参数中的 any
|
|
529
|
+
if (node.typeParameters.params) {
|
|
530
|
+
node.typeParameters.params.forEach(param => {
|
|
531
|
+
checkTypeNodeForAny(param, node)
|
|
532
|
+
})
|
|
533
|
+
}
|
|
534
|
+
} else {
|
|
535
|
+
// 如果没有 typeParameters 节点,检查源代码文本中是否包含泛型类型参数
|
|
536
|
+
// 这可能是因为 Vue SFC 中的 TypeScript 泛型没有被正确解析
|
|
537
|
+
if (callText.includes('<') && callText.includes('>')) {
|
|
538
|
+
// 提取泛型类型参数部分
|
|
539
|
+
const match = callText.match(/<([^>]+)>/)
|
|
540
|
+
if (match) {
|
|
541
|
+
const typeParamText = match[1]
|
|
542
|
+
// 检查类型参数文本中是否包含 "any"
|
|
543
|
+
if (typeParamText.includes('any')) {
|
|
544
|
+
// 报告 any 使用
|
|
545
|
+
anyUsages.push({
|
|
546
|
+
node: node,
|
|
547
|
+
type: 'explicit',
|
|
548
|
+
})
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* 检查表达式中是否使用了 any 类型的变量
|
|
558
|
+
*/
|
|
559
|
+
function checkExpressionForAnyVariableUse(node) {
|
|
560
|
+
if (!node) return
|
|
561
|
+
|
|
562
|
+
// 检查标识符的使用
|
|
563
|
+
if (node.type === 'Identifier') {
|
|
564
|
+
if (anyVariables.has(node.name)) {
|
|
565
|
+
// 使用了 any 类型的变量,计数一次
|
|
566
|
+
anyUsages.push({
|
|
567
|
+
node: node,
|
|
568
|
+
type: 'explicit',
|
|
569
|
+
})
|
|
570
|
+
}
|
|
571
|
+
return // 标识符不需要进一步递归检查
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// 检查成员表达式(如 param.id)
|
|
575
|
+
if (node.type === 'MemberExpression') {
|
|
576
|
+
// 检查对象是否是标识符
|
|
577
|
+
if (node.object.type === 'Identifier') {
|
|
578
|
+
if (anyVariables.has(node.object.name)) {
|
|
579
|
+
// 使用了 any 类型的变量,计数一次
|
|
580
|
+
anyUsages.push({
|
|
581
|
+
node: node,
|
|
582
|
+
type: 'explicit',
|
|
583
|
+
})
|
|
584
|
+
}
|
|
585
|
+
} else {
|
|
586
|
+
// 如果对象不是标识符,递归检查对象
|
|
587
|
+
checkExpressionForAnyVariableUse(node.object)
|
|
588
|
+
}
|
|
589
|
+
// 检查属性(如果不是标识符)
|
|
590
|
+
if (node.property && node.property.type !== 'Identifier') {
|
|
591
|
+
checkExpressionForAnyVariableUse(node.property)
|
|
592
|
+
}
|
|
593
|
+
return // 成员表达式检查完成
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// 递归检查子表达式
|
|
597
|
+
if (node.type === 'CallExpression') {
|
|
598
|
+
if (node.callee) {
|
|
599
|
+
checkExpressionForAnyVariableUse(node.callee)
|
|
600
|
+
}
|
|
601
|
+
if (node.arguments) {
|
|
602
|
+
node.arguments.forEach(arg => {
|
|
603
|
+
checkExpressionForAnyVariableUse(arg)
|
|
604
|
+
})
|
|
605
|
+
}
|
|
606
|
+
return
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
if (node.type === 'ReturnStatement' && node.argument) {
|
|
610
|
+
checkExpressionForAnyVariableUse(node.argument)
|
|
611
|
+
return
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
if (node.type === 'ObjectExpression' && node.properties) {
|
|
615
|
+
node.properties.forEach(prop => {
|
|
616
|
+
if (prop.value) {
|
|
617
|
+
checkExpressionForAnyVariableUse(prop.value)
|
|
618
|
+
}
|
|
619
|
+
})
|
|
620
|
+
return
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
if (node.type === 'ArrayExpression' && node.elements) {
|
|
624
|
+
node.elements.forEach(element => {
|
|
625
|
+
if (element) {
|
|
626
|
+
checkExpressionForAnyVariableUse(element)
|
|
627
|
+
}
|
|
628
|
+
})
|
|
629
|
+
return
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
if (node.type === 'BinaryExpression') {
|
|
633
|
+
checkExpressionForAnyVariableUse(node.left)
|
|
634
|
+
checkExpressionForAnyVariableUse(node.right)
|
|
635
|
+
return
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
if (node.type === 'UnaryExpression' && node.argument) {
|
|
639
|
+
checkExpressionForAnyVariableUse(node.argument)
|
|
640
|
+
return
|
|
641
|
+
}
|
|
536
642
|
}
|
|
537
643
|
|
|
538
644
|
/**
|
|
@@ -551,10 +657,10 @@ module.exports = {
|
|
|
551
657
|
function checkAnyUsageCount() {
|
|
552
658
|
if (shouldExcludeFile()) return
|
|
553
659
|
if (anyUsages.length > maxAnyCount) {
|
|
554
|
-
//
|
|
555
|
-
const
|
|
660
|
+
// 报告最后一个超出限制的 any 使用
|
|
661
|
+
const lastExceededUsage = anyUsages[anyUsages.length - 1]
|
|
556
662
|
context.report({
|
|
557
|
-
node:
|
|
663
|
+
node: lastExceededUsage.node,
|
|
558
664
|
messageId: 'exceedMaxAnyCount',
|
|
559
665
|
data: {
|
|
560
666
|
maxAnyCount,
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 检查是否是内置函数
|
|
3
|
+
* @param {import('@typescript-eslint/types').TSESTree.CallExpression} node - 调用表达式节点
|
|
4
|
+
* @returns {boolean} 是否是内置函数
|
|
5
|
+
*/
|
|
6
|
+
function isBuiltInFunction(node) {
|
|
7
|
+
if (node.type !== 'CallExpression') return false
|
|
8
|
+
|
|
9
|
+
// 检查全局对象的方法调用,如 JSON.parse, Promise.all 等
|
|
10
|
+
if (node.callee.type === 'MemberExpression' && node.callee.object.type === 'Identifier') {
|
|
11
|
+
const objectName = node.callee.object.name
|
|
12
|
+
|
|
13
|
+
// 内置全局对象列表 - 这些对象的所有方法都认为是内置的
|
|
14
|
+
const builtInObjects = ['JSON', 'Promise', 'Array', 'Object', 'String', 'Number', 'Boolean', 'Date', 'Math', 'RegExp', 'Map', 'Set', 'WeakMap', 'WeakSet']
|
|
15
|
+
|
|
16
|
+
if (builtInObjects.includes(objectName)) {
|
|
17
|
+
// 对于这些内置对象,假设它们的所有方法都是内置的
|
|
18
|
+
// 这样可以避免维护庞大的方法列表
|
|
19
|
+
return true
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// 检查可选链调用,如 spotArr?.find()
|
|
24
|
+
if (node.callee.type === 'ChainExpression' && node.callee.expression.type === 'CallExpression') {
|
|
25
|
+
const chainCall = node.callee.expression
|
|
26
|
+
if (chainCall.callee.type === 'MemberExpression' && chainCall.callee.object.type === 'Identifier') {
|
|
27
|
+
const objectName = chainCall.callee.object.name
|
|
28
|
+
|
|
29
|
+
// 内置全局对象列表
|
|
30
|
+
const builtInObjects = ['JSON', 'Promise', 'Array', 'Object', 'String', 'Number', 'Boolean', 'Date', 'Math', 'RegExp', 'Map', 'Set', 'WeakMap', 'WeakSet']
|
|
31
|
+
|
|
32
|
+
if (builtInObjects.includes(objectName)) {
|
|
33
|
+
// 对于这些内置对象,假设它们的所有方法都是内置的
|
|
34
|
+
return true
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// 检查全局函数调用,如 parseInt, parseFloat, isNaN 等
|
|
40
|
+
if (node.callee.type === 'Identifier') {
|
|
41
|
+
const functionName = node.callee.name
|
|
42
|
+
const globalFunctions = [
|
|
43
|
+
'parseInt', 'parseFloat', 'isNaN', 'isFinite', 'eval', 'encodeURI',
|
|
44
|
+
'encodeURIComponent', 'decodeURI', 'decodeURIComponent', 'escape', 'unescape'
|
|
45
|
+
]
|
|
46
|
+
return globalFunctions.includes(functionName)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return false
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 检查是否是简单的集合实例化(如 new Map(), new Set())
|
|
54
|
+
* 这些可以通过上下文推导类型,不应该算作隐式 any
|
|
55
|
+
* @param {import('@typescript-eslint/types').TSESTree.NewExpression} node - new 表达式节点
|
|
56
|
+
* @returns {boolean} 是否是简单的集合实例化
|
|
57
|
+
*/
|
|
58
|
+
function isSimpleCollectionInstantiation(node) {
|
|
59
|
+
if (!node) return false
|
|
60
|
+
|
|
61
|
+
// 检查 new Map() 或 new Set()
|
|
62
|
+
if (node.type === 'NewExpression') {
|
|
63
|
+
const calleeName = node.callee.type === 'Identifier' ? node.callee.name : ''
|
|
64
|
+
if (calleeName === 'Map' || calleeName === 'Set') {
|
|
65
|
+
// 检查是否有参数(无参数的 new Map() 或 new Set() 是简单的实例化)
|
|
66
|
+
if (!node.arguments || node.arguments.length === 0) {
|
|
67
|
+
return true
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return false
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
module.exports = {
|
|
76
|
+
isBuiltInFunction,
|
|
77
|
+
isSimpleCollectionInstantiation
|
|
78
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@foundbyte/eslint-plugin",
|
|
3
|
-
"version": "1.1.4-alpha.
|
|
3
|
+
"version": "1.1.4-alpha.3",
|
|
4
4
|
"description": "ESLint plugin for enforcing code style rules including arrow function padding, Vue script lines limit, and TypeScript type comment requirements",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|