docs: 归档 demo 工程样例与插件实测报告
- 新增三个 demo 工程(demo-eslint / demo-sqlfluff / demo-stylelint):各含 src/ 样例代码与覆盖率映射(run-coverage.mjs、coverage_map.json、stylelint_line_map.json) - 归档各 demo 的插件实测导出报告(reports/): - demo-eslint:common.js / esm-demo.js / legacysyntax.cjs / typescript.ts 审查报告 + 覆盖率报告(292/292 条零偏差,126 种规则全覆盖) - demo-sqlfluff:11 个 SQL 样例审查报告 + 覆盖率报告(113/113 条零偏差,57 种规则全覆盖,含 JJ01 行号缺陷修复后的复测验证) - demo-stylelint:common.css / empty-source.css 审查报告 + 覆盖率报告(145/145 条零偏差,68 种规则全覆盖)
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"@eslint/js": "^10.0.1",
|
||||
"typescript-eslint": "^8.66.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,662 @@
|
||||
# 代码审查报告
|
||||
|
||||
**文件:** `data\demo-eslint\src\common.js`
|
||||
**语言:** javascript
|
||||
**耗时:** 288.0s
|
||||
**分析工具:** eslint
|
||||
|
||||
---
|
||||
|
||||
总计: 214 | 错误: 197 | 警告: 17 | 建议: 0
|
||||
|
||||
静态分析 · 210 个问题
|
||||
|
||||
- 🔴 `eslint:require-yield` L12
|
||||
L12: 此生成器函数没有 'yield'。
|
||||
建议: 在生成器函数体内添加 yield 表达式;若本意不是生成器,请改为普通函数。
|
||||
- 🔴 `eslint:no-unused-vars` L12
|
||||
L12: 'generatorWithoutYield' 已定义但从未使用。
|
||||
建议: 删除该函数声明;若它应被外部使用,请添加导出或实际调用。
|
||||
- 🔴 `eslint:no-undef` L13
|
||||
L13: 未定义变量 'console'。
|
||||
建议: 在 ESLint 配置中声明浏览器或 Node 环境,或在文件顶部添加 /* global console */。
|
||||
- 🔴 `eslint:no-unused-vars` L17
|
||||
L17: 'asyncExecutor' 被赋值但从未使用。
|
||||
建议: 删除该变量,或在使用处引用它。
|
||||
- 🔴 `eslint:no-async-promise-executor` L17
|
||||
L17: Promise 执行器不应是 async 函数。
|
||||
建议: 移除 async 关键字,改为普通 executor,并使用 resolve/reject 控制 Promise 状态。
|
||||
- 🔴 `eslint:no-unused-vars` L17
|
||||
L17: 'reject' 已定义但从未使用。
|
||||
建议: 若不需要拒绝路径,可省略 reject 参数,或实际调用 reject。
|
||||
- 🔴 `eslint:no-unused-vars` L22
|
||||
L22: 'executorReturn' 被赋值但从未使用。
|
||||
建议: 删除该变量,或在使用处引用它。
|
||||
- 🔴 `eslint:no-unused-vars` L22
|
||||
L22: 'reject' 已定义但从未使用。
|
||||
建议: 若不需要拒绝路径,可省略 reject 参数,或实际调用 reject。
|
||||
- 🔴 `eslint:no-promise-executor-return` L23
|
||||
L23: Promise 执行器的返回值无法被读取。
|
||||
建议: 不要在 executor 内 return,直接调用 resolve 即可。
|
||||
- 🔴 `eslint:no-unused-vars` L27
|
||||
L27: 'awaitInLoop' 已定义但从未使用。
|
||||
建议: 删除该函数,或在使用处调用它。
|
||||
- 🔴 `eslint:no-undef` L30
|
||||
L30: 未定义变量 'fetch'。
|
||||
建议: 确保运行环境为浏览器或 Node 18+,并在 ESLint 配置中声明对应全局变量。
|
||||
- 🔴 `eslint:no-unused-vars` L37
|
||||
L37: 'returnAssign' 已定义但从未使用。
|
||||
建议: 删除该函数,或在使用处调用它。
|
||||
- 🔴 `eslint:no-return-assign` L39
|
||||
L39: 返回语句中不应包含赋值。
|
||||
建议: 将赋值与返回分开,避免歧义。
|
||||
- 🔴 `eslint:no-unused-vars` L39
|
||||
L39: 'assignedInReturn' 被赋值但从未使用。
|
||||
建议: 若不需要该局部变量,可以直接 return x。
|
||||
- 🔴 `eslint:no-unused-vars` L43
|
||||
L43: 'throwLiteral' 已定义但从未使用。
|
||||
建议: 删除该函数,或在使用处调用它。
|
||||
- 🔴 `eslint:no-throw-literal` L44
|
||||
L44: 应抛出错误对象。
|
||||
建议: 使用 new Error() 抛错,以保留堆栈信息。
|
||||
- 🔴 `eslint:no-unused-vars` L48
|
||||
L48: 'uselessReturn' 已定义但从未使用。
|
||||
建议: 删除该函数,或在使用处调用它。
|
||||
- 🔴 `eslint:no-undef` L54
|
||||
L54: 未定义变量 'console'。
|
||||
建议: 在 ESLint 配置中声明浏览器或 Node 环境,或在文件顶部添加 /* global console */。
|
||||
- 🔴 `eslint:no-unused-vars` L62
|
||||
L62: 'BadConstructor' 已定义但从未使用。
|
||||
建议: 删除该类,或在使用处实例化。
|
||||
- 🔴 `eslint:constructor-super` L63
|
||||
L63: 继承类构造函数中应调用 super()。
|
||||
建议: 在构造函数体内最先调用 super(message)。
|
||||
- 🔴 `eslint:no-undef` L64
|
||||
L64: 未定义变量 'console'。
|
||||
建议: 在 ESLint 配置中声明浏览器或 Node 环境,或在文件顶部添加 /* global console */。
|
||||
- 🔴 `eslint:no-class-assign` L70
|
||||
L70: 'Widget' 是类,不能重新赋值。
|
||||
建议: 删除对类名的赋值,或用其他变量名保存覆盖后的值。
|
||||
- 🔴 `eslint:no-unused-vars` L70
|
||||
L70: 'Widget' 被赋值但从未使用。
|
||||
建议: 删除该类或在使用处实例化。
|
||||
- 🔴 `eslint:no-unused-vars` L73
|
||||
L73: 'DupeMember' 已定义但从未使用。
|
||||
建议: 删除该类,或在使用处实例化。
|
||||
- 🔴 `eslint:no-dupe-class-members` L77
|
||||
L77: 重复的成员名 'method'。
|
||||
建议: 删除重复的方法定义,或改为不同方法名。
|
||||
- 🔴 `eslint:no-unused-vars` L83
|
||||
L83: 'EmptyStatic' 已定义但从未使用。
|
||||
建议: 删除该类,或在使用处实例化。
|
||||
- 🔴 `eslint:no-empty-static-block` L84
|
||||
L84: 不应有空的 static 块。
|
||||
建议: 删除空的 static 块,或在其中编写有意义的初始化逻辑。
|
||||
- 🔴 `eslint:no-unused-vars` L88
|
||||
L88: 'SetterReturn' 已定义但从未使用。
|
||||
建议: 删除该类,或在使用处实例化。
|
||||
- 🔴 `eslint:no-setter-return` L91
|
||||
L91: setter 不能返回值。
|
||||
建议: 删除 setter 中的 return 语句。
|
||||
- 🔴 `eslint:no-unused-vars` L96
|
||||
L96: 'GetterNoReturn' 已定义但从未使用。
|
||||
建议: 删除该类,或在使用处实例化。
|
||||
- 🔴 `eslint:getter-return` L97
|
||||
L97: getter 'value' 应返回值。
|
||||
建议: 在 getter 中返回一个有意义的表达式。
|
||||
- 🔴 `eslint:no-unused-vars` L98
|
||||
L98: 'x' 被赋值但从未使用。
|
||||
建议: 删除 x 声明,或在实际使用后返回它。
|
||||
- 🔴 `eslint:no-unused-vars` L103
|
||||
L103: 'ThisBeforeSuper' 已定义但从未使用。
|
||||
建议: 删除该类,或在使用处实例化。
|
||||
- 🔴 `eslint:no-this-before-super` L105
|
||||
L105: 在调用 super() 之前不允许使用 this。
|
||||
建议: 将 super(message) 移到 this.message 赋值之前。
|
||||
- 🔴 `eslint:no-unused-vars` L111
|
||||
L111: 'UnusedPrivate' 已定义但从未使用。
|
||||
建议: 删除该类,或在使用处实例化。
|
||||
- 🔴 `eslint:no-unused-private-class-members` L112
|
||||
L112: 私有成员 '#secret' 已定义但从未使用。
|
||||
建议: 删除 #secret 定义,或在类方法中实际使用它。
|
||||
- 🔴 `eslint:no-unused-vars` L123
|
||||
L123: 'looseEquality' 已定义但从未使用。
|
||||
建议: 删除该函数,或在使用处调用它。
|
||||
- 🔴 `eslint:eqeqeq` L124
|
||||
L124: 应使用 '===' 而非 '=='。
|
||||
建议: 将 == 改为 ===,避免隐式类型转换。
|
||||
- 🔴 `eslint:no-unused-vars` L128
|
||||
L128: 'nullEquality' 已定义但从未使用。
|
||||
建议: 删除该函数,或在使用处调用它。
|
||||
- 🔴 `eslint:no-eq-null` L129
|
||||
L129: 与 null 比较时应使用 '==='。
|
||||
建议: 如果只想匹配 null,应使用 x === null;如需要 null/undefined 都匹配,可显式写成 x === null || x === undefined。
|
||||
- 🔴 `eslint:eqeqeq` L129
|
||||
L129: 应使用 '===' 而非 '=='。
|
||||
建议: 将 == 改为 ===,避免隐式类型转换。
|
||||
- 🔴 `eslint:no-unused-vars` L133
|
||||
L133: 'selfCompare' 已定义但从未使用。
|
||||
建议: 删除该函数,或在使用处调用它。
|
||||
- 🔴 `eslint:no-self-compare` L134
|
||||
L134: 与自身比较可能没有意义。
|
||||
建议: 删除该比较;若想检测 NaN 应使用 Number.isNaN(x)。
|
||||
- 🔴 `eslint:no-unused-vars` L138
|
||||
L138: 'negZero' 已定义但从未使用。
|
||||
建议: 删除该函数,或在使用处调用它。
|
||||
- 🔴 `eslint:no-compare-neg-zero` L139
|
||||
L139: 不要使用 '===' 与 -0 比较。
|
||||
建议: 如果需要判断 -0,请使用 Object.is(x, -0)。
|
||||
- 🔴 `eslint:no-unused-vars` L143
|
||||
L143: 'isNanWrong' 已定义但从未使用。
|
||||
建议: 删除该函数,或在使用处调用它。
|
||||
- 🔴 `eslint:use-isnan` L144
|
||||
L144: 与 NaN 比较时应使用 isNaN 函数。
|
||||
建议: 使用 Number.isNaN(x) 进行 NaN 判断。
|
||||
- 🔴 `eslint:no-unused-vars` L148
|
||||
L148: 'typeofCheck' 已定义但从未使用。
|
||||
建议: 删除该函数,或在使用处调用它。
|
||||
- 🔴 `eslint:valid-typeof` L149
|
||||
L149: typeof 比较值无效。
|
||||
建议: 将 'strnig' 修正为 'string'。
|
||||
- 🔴 `eslint:no-unused-vars` L153
|
||||
L153: 'constBinary' 已定义但从未使用。
|
||||
建议: 删除该变量,或在使用处引用它。
|
||||
- 🔴 `eslint:no-unused-vars` L171
|
||||
L171: 'fallthrough' 已定义但从未使用。
|
||||
建议: 删除该函数,或在使用处调用它。
|
||||
- 🔴 `eslint:no-undef` L174
|
||||
L174: 未定义变量 'console'。
|
||||
建议: 在 ESLint 配置中声明浏览器或 Node 环境,或在文件顶部添加 /* global console */。
|
||||
- 🔴 `eslint:no-fallthrough` L175
|
||||
L175: 在 'case' 之前应有 'break' 语句。
|
||||
建议: 在 case 1 的语句后添加 break,除非刻意需要穿透。
|
||||
- 🔴 `eslint:no-undef` L176
|
||||
L176: 未定义变量 'console'。
|
||||
建议: 在 ESLint 配置中声明浏览器或 Node 环境,或在文件顶部添加 /* global console */。
|
||||
- 🔴 `eslint:no-unused-vars` L184
|
||||
L184: 'caseDeclarations' 已定义但从未使用。
|
||||
建议: 删除该函数,或在使用处调用它。
|
||||
- 🔴 `eslint:no-case-declarations` L187
|
||||
L187: case 块中不应有词法声明。
|
||||
建议: 为 case 1 添加块级大括号 {},或把 let 声明移到 switch 外。
|
||||
- 🔴 `eslint:no-undef` L188
|
||||
L188: 未定义变量 'console'。
|
||||
建议: 在 ESLint 配置中声明浏览器或 Node 环境,或在文件顶部添加 /* global console */。
|
||||
- 🔴 `eslint:no-unused-vars` L196
|
||||
L196: 'duplicateCase' 已定义但从未使用。
|
||||
建议: 删除该函数,或在使用处调用它。
|
||||
- 🔴 `eslint:no-duplicate-case` L200
|
||||
L200: 重复的 case 标签。
|
||||
建议: 将第二个 case 1 改为其他值,或删除重复分支。
|
||||
- 🔴 `eslint:no-unused-vars` L208
|
||||
L208: 'dupeElseIf' 已定义但从未使用。
|
||||
建议: 删除该函数,或在使用处调用它。
|
||||
- 🔴 `eslint:no-dupe-else-if` L211
|
||||
L211: 此分支永远无法执行,其条件与前序 if-else-if 条件重复或已被覆盖。
|
||||
建议: 删除重复的 else if 分支,或改为不同的条件。
|
||||
- 🔴 `eslint:no-unused-vars` L218
|
||||
L218: 'noDefault' 已定义但从未使用。
|
||||
建议: 删除该函数,或在使用处调用它。
|
||||
- 🔴 `eslint:default-case` L219
|
||||
L219: 应包含 default 分支。
|
||||
建议: 在 switch 中添加 default 分支,处理未匹配的情况。
|
||||
- 🔴 `eslint:no-unused-vars` L227
|
||||
L227: 'defaultNotLast' 已定义但从未使用。
|
||||
建议: 删除该函数,或在使用处调用它。
|
||||
- 🔴 `eslint:default-case-last` L229
|
||||
L229: default 分支应放在最后。
|
||||
建议: 将 default 分支移到 switch 的最后一个分支。
|
||||
- 🔴 `eslint:no-func-assign` L246
|
||||
L246: 'assignableFn' 是函数,不能重新赋值。
|
||||
建议: 删除对函数名的重新赋值,或将新函数赋值给其他变量名。
|
||||
- 🔴 `eslint:no-unused-vars` L246
|
||||
L246: 'assignableFn' 被赋值但从未使用。
|
||||
建议: 删除该函数定义或在使用处调用它。
|
||||
- 🔴 `eslint:no-unused-vars` L256
|
||||
L256: 'forDirection' 已定义但从未使用。
|
||||
建议: 删除该函数,或在使用处调用它。
|
||||
- 🔴 `eslint:for-direction` L257
|
||||
L257: 循环更新子句使变量向错误方向移动。
|
||||
建议: 将 i-- 改为 i++,使循环条件 i < 10 最终为 false。
|
||||
- 🔴 `eslint:no-undef` L258
|
||||
L258: 未定义变量 'console'。
|
||||
建议: 在 ESLint 配置中声明浏览器或 Node 环境,或在文件顶部添加 /* global console */。
|
||||
- 🔴 `eslint:no-unused-vars` L267
|
||||
L267: 'my_snake_case_var' 被赋值但从未使用。
|
||||
建议: 删除该变量,或在使用处引用它。
|
||||
- 🔴 `eslint:no-unused-vars` L270
|
||||
L270: 'generated' 被赋值但从未使用。
|
||||
建议: 删除该变量,或在使用处引用它。
|
||||
- 🔴 `eslint:no-undef` L270
|
||||
L270: 未定义变量 'object'。
|
||||
建议: 检查全局是否存在 object 构造函数;更推荐使用 {} 创建空对象。
|
||||
- 🔴 `eslint:no-unused-vars` L273
|
||||
L273: 'uselessConcat' 被赋值但从未使用。
|
||||
建议: 删除该变量,或在使用处引用它。
|
||||
- 🔴 `eslint:no-unused-vars` L276
|
||||
L276: 'obj' 被赋值但从未使用。
|
||||
建议: 删除该变量,或在使用处引用它。
|
||||
- 🔴 `eslint:no-unused-vars` L279
|
||||
L279: 'dupeKeyObj' 被赋值但从未使用。
|
||||
建议: 删除该变量,或在使用处引用它。
|
||||
- 🔴 `eslint:no-dupe-keys` L279
|
||||
L279: 属性名 'a' 重复。
|
||||
建议: 删除重复的 a 属性,保留最终需要的值。
|
||||
- 🔴 `eslint:no-unused-vars` L282
|
||||
L282: 'a' 被赋值但从未使用。
|
||||
建议: 删除该解构赋值,或在使用处引用 a。
|
||||
- 🔴 `eslint:no-unused-vars` L287
|
||||
L287: 'preferTemplate' 已定义但从未使用。
|
||||
建议: 删除该函数,或在使用处调用它。
|
||||
- 🔴 `eslint:no-unused-vars` L292
|
||||
L292: 'preferSpreadStyle' 已定义但从未使用。
|
||||
建议: 删除该函数,或在使用处调用它。
|
||||
- 🔴 `eslint:no-unused-vars` L298
|
||||
L298: 'preferRest' 已定义但从未使用。
|
||||
建议: 删除该函数,或在使用处调用它。
|
||||
- 🔴 `eslint:no-unused-vars` L307
|
||||
L307: 'preferSpreadCall' 已定义但从未使用。
|
||||
建议: 删除该函数,或在使用处调用它。
|
||||
- 🔴 `eslint:no-unused-vars` L313
|
||||
L313: 'hasOwnPrefer' 已定义但从未使用。
|
||||
建议: 删除该函数,或在使用处调用它。
|
||||
- 🔴 `eslint:no-shadow` L313
|
||||
L313: 'obj' 与第 276 行第 7 列的上层作用域中的 'obj' 重名。
|
||||
建议: 将参数改名为 target 或 sourceObj,避免遮蔽外层变量。
|
||||
- 🔴 `eslint:no-unused-vars` L318
|
||||
L318: 'preferConstDemo' 已定义但从未使用。
|
||||
建议: 删除该函数,或在使用处调用它。
|
||||
- 🔴 `eslint:no-useless-assignment` L323
|
||||
L323: 此赋值在后续语句中未被使用。
|
||||
建议: 删除 someObj 声明,或在实际使用后保留。
|
||||
- 🔴 `eslint:no-unused-vars` L334
|
||||
L334: 'extendNative' 已定义但从未使用。
|
||||
建议: 删除该函数,或在使用处调用它。
|
||||
- 🔴 `eslint:no-extend-native` L335
|
||||
L335: String 原型是只读的,不应添加属性。
|
||||
建议: 不要修改原生原型;如需扩展功能,应使用包装函数或 WeakMap。
|
||||
- 🔴 `eslint:no-unused-vars` L339
|
||||
L339: 'evalDemo' 已定义但从未使用。
|
||||
建议: 删除该函数,或在使用处调用它。
|
||||
- 🔴 `eslint:no-eval` L340
|
||||
L340: eval 可能存在安全风险。
|
||||
建议: 避免使用 eval;如果只是解析 JSON,请用 JSON.parse。
|
||||
- 🔴 `eslint:no-unused-vars` L344
|
||||
L344: 'paramReassign' 已定义但从未使用。
|
||||
建议: 删除该函数,或在使用处调用它。
|
||||
- 🔴 `eslint:no-unused-vars` L350
|
||||
L350: 'unmodifiedLoop' 已定义但从未使用。
|
||||
建议: 删除该函数,或在使用处调用它。
|
||||
- 🔴 `eslint:no-unmodified-loop-condition` L351
|
||||
L351: 循环中 'i' 未被修改。
|
||||
建议: 在循环体内修改 i,否则该循环可能无限执行。
|
||||
- 🔴 `eslint:no-undef` L352
|
||||
L352: 未定义变量 'console'。
|
||||
建议: 在 ESLint 配置中声明浏览器或 Node 环境,或在文件顶部添加 /* global console */。
|
||||
- 🔴 `eslint:no-unused-vars` L357
|
||||
L357: 'unreachableLoop' 已定义但从未使用。
|
||||
建议: 删除该函数,或在使用处调用它。
|
||||
- 🔴 `eslint:no-unreachable-loop` L359
|
||||
L359: 无效循环,其循环体只能执行一次。
|
||||
建议: 删除循环或移除循环体中的 break。
|
||||
- 🔴 `eslint:no-unused-vars` L359
|
||||
L359: 'item' 被赋值但从未使用。
|
||||
建议: 若不需要 item,改用索引循环或 for...of 中实际使用 item。
|
||||
- 🔴 `eslint:no-unreachable-loop` L362
|
||||
L362: 无效循环,其循环体只能执行一次。
|
||||
建议: 将 while(true) 改为 if(true),或移除 break 并重写逻辑。
|
||||
- 🔴 `eslint:no-unused-vars` L376
|
||||
L376: 'badRegex' 被赋值但从未使用。
|
||||
建议: 删除该变量,或在使用处引用它。
|
||||
- 🔴 `eslint:no-invalid-regexp` L376
|
||||
L376: 无效正则表达式:/[/: 未结束的字符类。
|
||||
建议: 如果要匹配左括号,应使用 new RegExp('\\[')。
|
||||
- 🔴 `eslint:no-unused-vars` L379
|
||||
L379: 'regexSpaces' 被赋值但从未使用。
|
||||
建议: 删除该变量,或在使用处引用它。
|
||||
- 🔴 `eslint:no-regex-spaces` L379
|
||||
L379: 空格难以计数,请使用 {2}。
|
||||
建议: 将连续空格改为 {2} 量词。
|
||||
- 🔴 `eslint:no-unused-vars` L382
|
||||
L382: 'controlRegex' 被赋值但从未使用。
|
||||
建议: 删除该变量,或在使用处引用它。
|
||||
- 🔴 `eslint:no-control-regex` L382
|
||||
L382: 正则表达式中出现意外的控制字符:\x1f。
|
||||
建议: 如果确实要匹配控制字符,请添加注释并临时禁用该规则;否则应移除该正则。
|
||||
- 🔴 `eslint:no-unused-vars` L385
|
||||
L385: 'emptyCharClass' 被赋值但从未使用。
|
||||
建议: 删除该变量,或在使用处引用它。
|
||||
- 🔴 `eslint:no-empty-character-class` L385
|
||||
L385: 空字符类。
|
||||
建议: 删除空的 [],或写入实际需要的字符。
|
||||
- 🔴 `eslint:no-unused-vars` L388
|
||||
L388: 'misleadingCharClass' 被赋值但从未使用。
|
||||
建议: 删除该变量,或在使用处引用它。
|
||||
- 🔴 `eslint:no-misleading-character-class` L388
|
||||
L388: 字符类中出现意外的代理对,请使用 'u' 标志。
|
||||
建议: 为正则添加 u 标志。
|
||||
- 🔴 `eslint:no-misleading-character-class` L388
|
||||
L388: 字符类中出现意外的代理对,请使用 'u' 标志。
|
||||
建议: 为正则添加 u 标志。
|
||||
- 🔴 `eslint:no-unused-vars` L391
|
||||
L391: 'uselessBackref' 被赋值但从未使用。
|
||||
建议: 删除该变量,或在使用处引用它。
|
||||
- 🔴 `eslint:no-useless-backreference` L391
|
||||
L391: 反向引用 '\1' 将被忽略,它引用了自身所在的组 '(\1a)'。
|
||||
建议: 删除无意义的反向引用,或重写分组。
|
||||
- 🔴 `eslint:no-unused-vars` L394
|
||||
L394: 'uselessEscape' 被赋值但从未使用。
|
||||
建议: 删除该变量,或在使用处引用它。
|
||||
- 🔴 `eslint:no-useless-escape` L394
|
||||
L394: 多余的转义字符:\.。
|
||||
建议: 删除多余的转义,写成 'line.'。
|
||||
- 🔴 `eslint:no-unused-vars` L397
|
||||
L397: 'irregularWhitespace' 被赋值但从未使用。
|
||||
建议: 删除该变量,或在使用处引用它。
|
||||
- 🔴 `eslint:no-loss-of-precision` L400
|
||||
L400: 此数字字面量在运行时将丢失精度。
|
||||
建议: 使用可安全表示的浮点字面量,或改用 BigInt/字符串处理高精度数值。
|
||||
- 🔴 `eslint:no-sparse-arrays` L403
|
||||
L403: 数组中不应出现空槽位。
|
||||
建议: 用 undefined 显式填充空位,或删除该槽位。
|
||||
- 🔴 `eslint:no-unused-vars` L406
|
||||
L406: 'emptyFunctionBody' 已定义但从未使用。
|
||||
建议: 删除该函数,或在使用处调用它。
|
||||
- 🔴 `eslint:no-empty` L409
|
||||
L409: 空块语句。
|
||||
建议: 在 if 块中填充逻辑,或删除空的 if 语句。
|
||||
- 🔴 `eslint:no-unused-vars` L413
|
||||
L413: 'constantBinary' 被赋值但从未使用。
|
||||
建议: 删除该变量,或在使用处引用它。
|
||||
- 🔴 `eslint:no-constant-binary-expression` L413
|
||||
L413: '&&' 表达式的左侧出现意外的常量真值。
|
||||
建议: 移除恒真左侧,或改写为普通值。
|
||||
- 🔴 `eslint:no-constant-condition` L416
|
||||
L416: 意外的常量条件。
|
||||
建议: 删除固定的 if(true),或改为真实条件。
|
||||
- 🔴 `eslint:no-undef` L417
|
||||
L417: 未定义变量 'console'。
|
||||
建议: 在 ESLint 配置中声明浏览器或 Node 环境,或在文件顶部添加 /* global console */。
|
||||
- 🔴 `eslint:no-cond-assign` L422
|
||||
L422: 应是条件表达式,而不是赋值。
|
||||
建议: 将 = 改为 ===,或者把赋值移到 if 之前。
|
||||
- 🔴 `eslint:no-constant-condition` L422
|
||||
L422: 意外的常量条件。
|
||||
建议: 修正条件赋值后,条件将不再是常量。
|
||||
- 🔴 `eslint:no-unused-vars` L422
|
||||
L422: 'condVar' 被赋值但从未使用。
|
||||
建议: 删除该变量,或在实际条件中使用它。
|
||||
- 🔴 `eslint:no-undef` L423
|
||||
L423: 未定义变量 'console'。
|
||||
建议: 在 ESLint 配置中声明浏览器或 Node 环境,或在文件顶部添加 /* global console */。
|
||||
- 🔴 `eslint:no-unused-vars` L428
|
||||
L428: 'selfAssigned' 被赋值但从未使用。
|
||||
建议: 删除该变量,或在使用处引用它。
|
||||
- 🔴 `eslint:no-useless-assignment` L428
|
||||
L428: 此赋值在后续语句中未被使用。
|
||||
建议: 删除自赋值语句。
|
||||
- 🔴 `eslint:no-self-assign` L428
|
||||
L428: 'selfAssigned' 被赋值为自身。
|
||||
建议: 删除该无意义的自赋值。
|
||||
- 🔴 `eslint:no-debugger` L431
|
||||
L431: 不应使用 'debugger' 语句。
|
||||
建议: 删除 debugger 语句。
|
||||
- 🔴 `eslint:no-extra-boolean-cast` L434
|
||||
L434: 多余的双重否定。
|
||||
建议: 移除 !!,直接使用变量作为条件。
|
||||
- 🔴 `eslint:no-undef` L435
|
||||
L435: 未定义变量 'console'。
|
||||
建议: 在 ESLint 配置中声明浏览器或 Node 环境,或在文件顶部添加 /* global console */。
|
||||
- 🔴 `eslint:no-ex-assign` L442
|
||||
L442: 不应给 catch 异常参数赋值。
|
||||
建议: 不要重新赋值 e;如需重新抛出,请直接 throw e 或 throw new Error(..., { cause: e })。
|
||||
- 🔴 `eslint:no-unused-vars` L442
|
||||
L442: 'e' 被赋值但从未使用。
|
||||
建议: 删除赋值;如果需要新错误,直接抛出而不是赋值给 e。
|
||||
- 🔴 `eslint:no-unused-vars` L446
|
||||
L446: 'noEmptyPattern' 已定义但从未使用。
|
||||
建议: 删除该函数,或在使用处调用它。
|
||||
- 🔴 `eslint:no-empty-pattern` L446
|
||||
L446: 不应使用空对象解构模式。
|
||||
建议: 在解构模式中写出至少一个属性名,或直接使用普通参数。
|
||||
- 🔴 `eslint:no-unused-vars` L451
|
||||
L451: 'unreachableCode' 已定义但从未使用。
|
||||
建议: 删除该函数,或在使用处调用它。
|
||||
- 🔴 `eslint:no-unreachable` L453
|
||||
L453: 不可达代码。
|
||||
建议: 删除 return 之后永远不会执行的语句。
|
||||
- 🔴 `eslint:no-undef` L453
|
||||
L453: 未定义变量 'console'。
|
||||
建议: 在 ESLint 配置中声明浏览器或 Node 环境,或在文件顶部添加 /* global console */。
|
||||
- 🔴 `eslint:no-unused-vars` L457
|
||||
L457: 'unexpectedMultiline' 被赋值但从未使用。
|
||||
建议: 删除该变量,或在使用处引用它。
|
||||
- 🔴 `eslint:no-unexpected-multiline` L458
|
||||
L458: 函数与函数调用左括号之间存在意外换行。
|
||||
建议: 在前一行末尾补充分号,或将 IIFE 整体改成赋值表达式,避免被解释为对上文变量的调用。
|
||||
- 🔴 `eslint:no-undef` L459
|
||||
L459: 未定义变量 'console'。
|
||||
建议: 在 ESLint 配置中声明浏览器或 Node 环境,或在文件顶部添加 /* global console */。
|
||||
- 🔴 `eslint:no-unused-vars` L463
|
||||
L463: 'unsafeOptional' 已定义但从未使用。
|
||||
建议: 删除该函数,或在使用处调用它。
|
||||
- 🔴 `eslint:no-shadow` L463
|
||||
L463: 'obj' 与第 276 行第 7 列的上层作用域中的 'obj' 重名。
|
||||
建议: 将参数改名为 target 或 sourceObj,避免遮蔽外层变量。
|
||||
- 🔴 `eslint:no-unsafe-optional-chaining` L464
|
||||
L464: 可选链使用不安全;如果短路为 'undefined',后续运算会抛 TypeError。
|
||||
建议: 将后续属性访问也改为可选链,或先判断 obj?.a 是否存在。
|
||||
- 🔴 `eslint:no-unsafe-negation` L470
|
||||
L470: 不应取反 'in' 运算符的左操作数。
|
||||
建议: 将整个 in 表达式放入括号后再取反,例如 !(flagFlag in holderObj)。
|
||||
- 🔴 `eslint:no-undef` L471
|
||||
L471: 未定义变量 'console'。
|
||||
建议: 在 ESLint 配置中声明浏览器或 Node 环境,或在文件顶部添加 /* global console */。
|
||||
- 🔴 `eslint:no-unused-vars` L475
|
||||
L475: 'unsafeFinally' 已定义但从未使用。
|
||||
建议: 删除该函数,或在使用处调用它。
|
||||
- 🔴 `eslint:no-unsafe-finally` L479
|
||||
L479: 在 finally 中使用返回语句不安全。
|
||||
建议: 不要在 finally 中 return,否则会覆盖 try 中的 throw 或 return。
|
||||
- 🔴 `eslint:no-unused-labels` L484
|
||||
L484: 标签 'unusedLabel:' 已定义但从未使用。
|
||||
建议: 移除未使用的标签。
|
||||
- 🔴 `eslint:no-undef` L485
|
||||
L485: 未定义变量 'console'。
|
||||
建议: 在 ESLint 配置中声明浏览器或 Node 环境,或在文件顶部添加 /* global console */。
|
||||
- 🔴 `eslint:no-unused-vars` L489
|
||||
L489: 'completelyUnusedVariable' 被赋值但从未使用。
|
||||
建议: 删除该变量,或在使用处引用它。
|
||||
- 🔴 `eslint:no-unused-vars` L492
|
||||
L492: 'uselessCatch' 已定义但从未使用。
|
||||
建议: 删除该函数,或在使用处调用它。
|
||||
- 🔴 `eslint:no-useless-catch` L493
|
||||
L493: 不必要的 try/catch 包装。
|
||||
建议: 删除 try/catch,直接返回 try 中的值。
|
||||
- 🔴 `eslint:no-unreachable` L495
|
||||
L495: 不可达代码。
|
||||
建议: 删除 catch 中被 no-useless-catch 覆盖后不再可达的 throw 逻辑。
|
||||
- 🔴 `eslint:no-unused-vars` L514
|
||||
L514: 'irregularWS' 被赋值但从未使用。
|
||||
建议: 删除该变量,或在使用处引用它。
|
||||
- 🔴 `eslint:no-irregular-whitespace` L514
|
||||
L514: 不允许使用不规则空白。
|
||||
建议: 将行尾的不可见空白(如 NBSP)替换为普通空格或删除。
|
||||
- 🔴 `eslint:no-var` L521
|
||||
L521: 不应使用 var,请改用 let 或 const。
|
||||
建议: 改用 const,若需要重新赋值则改用 let。
|
||||
- 🔴 `eslint:no-unused-vars` L521
|
||||
L521: 'oldStyleVar' 被赋值但从未使用。
|
||||
建议: 删除该变量,或在使用处引用它。
|
||||
- 🔴 `eslint:no-undef` L524
|
||||
L524: 未定义变量 'console'。
|
||||
建议: 在 ESLint 配置中声明浏览器或 Node 环境,或在文件顶部添加 /* global console */。
|
||||
- 🔴 `eslint:no-undef` L524
|
||||
L524: 未定义变量 'undefinedGlobalThing'。
|
||||
建议: 检查该全局变量是否真实存在;若存在,请添加全局声明。
|
||||
- 🔴 `eslint:no-unused-vars` L527
|
||||
L527: 'redeclareDemo' 已定义但从未使用。
|
||||
建议: 删除该函数,或在使用处调用它。
|
||||
- 🔴 `eslint:no-var` L528
|
||||
L528: 不应使用 var,请改用 let 或 const。
|
||||
建议: 改用 let 或 const。
|
||||
- 🔴 `eslint:no-var` L529
|
||||
L529: 不应使用 var,请改用 let 或 const。
|
||||
建议: 删除重复声明,并保留一次用 let/const 的声明。
|
||||
- 🔴 `eslint:no-redeclare` L529
|
||||
L529: 'redeclaredName' 已定义。
|
||||
建议: 删除重复的 var 声明,只在函数中声明一次。
|
||||
- 🔴 `eslint:no-unused-vars` L529
|
||||
L529: 'redeclaredName' 被赋值但从未使用。
|
||||
建议: 保留一次声明并在使用处引用,或删除整个演示函数。
|
||||
- 🔴 `eslint:no-unused-vars` L539
|
||||
L539: 'deleteVarDemo' 已定义但从未使用。
|
||||
建议: 删除该函数,或在使用处调用它。
|
||||
- 🔴 `eslint:no-shadow` L540
|
||||
L540: 'obj' 与第 276 行第 7 列的上层作用域中的 'obj' 重名。
|
||||
建议: 将局部变量 obj 改名为 targetObj 或 instance。
|
||||
- 🔴 `eslint:no-unused-vars` L545
|
||||
L545: 'shadowRestricted' 已定义但从未使用。
|
||||
建议: 删除该函数,或在使用处调用它。
|
||||
- 🔴 `eslint:no-shadow-restricted-names` L545
|
||||
L545: 不允许遮蔽全局属性 'undefined'。
|
||||
建议: 将参数 undefined 改名为 other 或 anyValue。
|
||||
- 🔴 `eslint:no-unused-vars` L550
|
||||
L550: 'globalAssign' 已定义但从未使用。
|
||||
建议: 删除该函数,或在使用处调用它。
|
||||
- 🔴 `eslint:no-global-assign` L551
|
||||
L551: 只读全局变量 'Math' 不应被修改。
|
||||
建议: 不要给 Math 赋值;如果需要自定义数学方法,请使用新的命名空间。
|
||||
- 🔴 `eslint:no-unused-vars` L558
|
||||
L558: 'blockScopedDemo' 已定义但从未使用。
|
||||
建议: 删除该函数,或在使用处调用它。
|
||||
- 🔴 `eslint:no-constant-condition` L559
|
||||
L559: 意外的常量条件。
|
||||
建议: 移除 if(true),或改为基于真实条件。
|
||||
- 🔴 `eslint:no-var` L560
|
||||
L560: 不应使用 var,请改用 let 或 const。
|
||||
建议: 在函数顶部改用 let/const 声明,并确保块级语义正确。
|
||||
- 🔴 `eslint:no-undef` L562
|
||||
L562: 未定义变量 'console'。
|
||||
建议: 在 ESLint 配置中声明浏览器或 Node 环境,或在文件顶部添加 /* global console */。
|
||||
- 🔴 `eslint:block-scoped-var` L562
|
||||
L562: 在第 560 行声明的 'blockScoped' 在绑定上下文之外使用。
|
||||
建议: 将 var 移到函数顶部,或改用 let/const 并确保作用域一致。
|
||||
- 🔴 `eslint:no-unused-vars` L566
|
||||
L566: 'unassignedVarDemo' 已定义但从未使用。
|
||||
建议: 删除该函数,或在使用处调用它。
|
||||
- 🔴 `eslint:no-unassigned-vars` L567
|
||||
L567: 'unassignedVariable' 因从未赋值而始终为 'undefined'。
|
||||
建议: 初始化变量,或检查是否真的需要该变量。
|
||||
- 🔴 `eslint:no-unused-vars` L572
|
||||
L572: 'shadowDemo' 已定义但从未使用。
|
||||
建议: 删除该函数,或在使用处调用它。
|
||||
- 🔴 `eslint:no-unused-vars` L573
|
||||
L573: 'value' 被赋值但从未使用。
|
||||
建议: 删除该变量,或在使用处引用它。
|
||||
- 🔴 `eslint:no-shadow` L575
|
||||
L575: 'value' 与第 573 行第 7 列的上层作用域中的 'value' 重名。
|
||||
建议: 将内层变量改名为 innerValue。
|
||||
- 🔴 `eslint:no-unused-vars` L582
|
||||
L582: 'uselessAssignment' 已定义但从未使用。
|
||||
建议: 删除该函数,或在使用处调用它。
|
||||
- 🔴 `eslint:no-useless-assignment` L583
|
||||
L583: 此赋值在后续语句中未被使用。
|
||||
建议: 删除初始赋值,只保留最终赋值。
|
||||
- 🔴 `eslint:no-unused-vars` L589
|
||||
L589: 'arrayViaConstructor' 被赋值但从未使用。
|
||||
建议: 删除该变量,或在使用处引用它。
|
||||
- 🔴 `eslint:no-array-constructor` L589
|
||||
L589: 应优先使用数组字面量 []。
|
||||
建议: 改用 [1, 2, 3]。
|
||||
- 🔴 `eslint:no-unused-vars` L592
|
||||
L592: 'symbolViaNew' 被赋值但从未使用。
|
||||
建议: 删除该变量,或在使用处引用它。
|
||||
- 🔴 `eslint:no-new-native-nonconstructor` L592
|
||||
L592: 'Symbol' 不能作为构造函数调用。
|
||||
建议: 移除 new,直接调用 Symbol('x')。
|
||||
- 🔴 `eslint:no-unused-vars` L595
|
||||
L595: 'jsonAsFunction' 被赋值但从未使用。
|
||||
建议: 删除该变量,或在使用处引用它。
|
||||
- 🔴 `eslint:no-obj-calls` L595
|
||||
L595: 'JSON' 不是函数。
|
||||
建议: 不要直接调用 JSON();如需解析 JSON 字符串,应使用 JSON.parse()。
|
||||
- 🔴 `eslint:no-const-assign` L599
|
||||
L599: 'immutableValue' 是常量,不能重新赋值。
|
||||
建议: 改用 let,或删除赋值。
|
||||
- 🔴 `eslint:no-unused-vars` L599
|
||||
L599: 'immutableValue' 被赋值但从未使用。
|
||||
建议: 删除该变量,或在使用处引用它。
|
||||
- 🔴 `eslint:no-unused-vars` L602
|
||||
L602: 'protoBuilds' 已定义但从未使用。
|
||||
建议: 删除该函数,或在使用处调用它。
|
||||
- 🔴 `eslint:no-shadow` L602
|
||||
L602: 'obj' 与第 276 行第 7 列的上层作用域中的 'obj' 重名。
|
||||
建议: 将参数改名为 target 或 sourceObj,避免遮蔽外层变量。
|
||||
- 🔴 `eslint:no-prototype-builtins` L603
|
||||
L603: 不应从目标对象上访问 Object.prototype 的方法 'hasOwnProperty'。
|
||||
建议: 使用 Object.hasOwn(obj, 'key') 或 Object.prototype.hasOwnProperty.call(obj, 'key')。
|
||||
- 🟡 `eslint:no-await-in-loop` L30
|
||||
L30: 循环内不应使用 await。
|
||||
建议: 若请求之间无依赖,应使用 Promise.all 并行执行;若有依赖,至少在循环外积累 Promise。
|
||||
- 🟡 `eslint:no-useless-return` L50
|
||||
L50: 不必要的 return 语句。
|
||||
建议: 删除函数末尾无意义的 return。
|
||||
- 🟡 `eslint:prefer-template` L154
|
||||
L154: 不应使用字符串拼接。
|
||||
建议: 改用模板字符串或直接写出拼接结果;注意当前表达式结果可能是 'aNaN'。
|
||||
- 🟡 `eslint:camelcase` L267
|
||||
L267: 标识符 'my_snake_case_var' 不是驼峰命名。
|
||||
建议: 改为驼峰命名,如 mySnakeCaseVar。
|
||||
- 🟡 `eslint:new-cap` L270
|
||||
L270: 构造函数名不应以小写字母开头。
|
||||
建议: 如果意图是创建对象,应使用 new Object() 或对象字面量 {}。
|
||||
- 🟡 `eslint:no-useless-concat` L273
|
||||
L273: 不应拼接字符串字面量。
|
||||
建议: 直接写 'foobar'。
|
||||
- 🟡 `eslint:no-useless-computed-key` L276
|
||||
L276: 属性 ['key'] 的计算属性是不必要的。
|
||||
建议: 直接写成 key: 'value'。
|
||||
- 🟡 `eslint:no-useless-rename` L282
|
||||
L282: 解构赋值 a 被无必要地重命名。
|
||||
建议: 简化为 { a }。注意 someObj 当前在 L282 之后才声明,会导致运行时错误。
|
||||
- 🟡 `eslint:prefer-template` L288
|
||||
L288: 不应使用字符串拼接。
|
||||
建议: 改用模板字符串。
|
||||
- 🟡 `eslint:prefer-object-spread` L293
|
||||
L293: 应使用对象展开代替 Object.assign,例如 `{ ...foo }`。
|
||||
建议: 改用对象字面量展开语法。
|
||||
- 🟡 `eslint:prefer-rest-params` L299
|
||||
L299: 应使用剩余参数代替 'arguments'。
|
||||
建议: 将函数签名改为 function preferRest(...args),并直接返回 args。
|
||||
- 🟡 `eslint:prefer-spread` L309
|
||||
L309: 应使用展开运算符代替 '.apply()'。
|
||||
建议: 使用 spreadThis(...nums)。
|
||||
- 🟡 `eslint:prefer-object-has-own` L314
|
||||
L314: 应使用 Object.hasOwn() 代替 Object.prototype.hasOwnProperty.call()。
|
||||
建议: 改用 Object.hasOwn(obj, key)。
|
||||
- 🟡 `eslint:no-param-reassign` L345
|
||||
L345: 不应重新赋值函数参数 'x'。
|
||||
建议: 改用局部变量保存计算结果。
|
||||
- 🟡 `eslint:new-cap` L595
|
||||
L595: 以大写字母开头的函数只应作为构造函数使用。
|
||||
建议: JSON 不是构造函数,也不可调用;使用 JSON.parse 等静态方法。
|
||||
|
||||
AI 审查 · 4 条建议
|
||||
|
||||
- 🔴 [AI] [design] `top-level-fatal-expression` L270
|
||||
**顶层存在多个必然抛出异常的表达式,会导致脚本加载中断**
|
||||
合并文件在模块初始化阶段依次执行大量演示代码,包括 `new object()`(若 object 未定义会抛 ReferenceError)、`new RegExp('[')`(抛 SyntaxError)、`new Symbol('x')`(抛 TypeError)、`JSON()`(抛 TypeError)、`immutableValue = 2`(抛 TypeError)等。这些不是普通 lint 风格问题,而是脚本加载即崩溃的致命缺陷。即使静态分析逐条标记了相关规则,文件的“可运行性”仍无法保证。
|
||||
建议: 将演示/规则触发代码放入独立函数或测试用例中,避免在模块顶层直接执行会抛异常的字面量表达式;必要时用 try/catch 包住这些演示片段。
|
||||
- 🔴 [AI] [bug] `use-before-define` L282
|
||||
**someObj 在声明前使用导致暂时性死区错误**
|
||||
L282 的 `const { a: a } = someObj;` 引用了 L323 才声明的 `const someObj`。const 声明存在暂时性死区,代码执行到 L282 时会抛出 ReferenceError,导致合并脚本在后续逻辑执行前中断。
|
||||
建议: 将 `const someObj = ...` 移到 L282 之前,或在 L282 直接使用对象字面量,避免引用未初始化的变量。
|
||||
- 🟡 [AI] [bug] `string-coercion-bug` L154
|
||||
**字符串拼接 `-'b'` 产生 'aNaN',很可能不是预期结果**
|
||||
L154 的 `const y = 'a' + -'b';` 中 `-'b'` 的结果是 NaN,字符串拼接后得到 'aNaN'。若意图是拼接两个字符串,应直接写 'ab';若意图是数学运算,则操作数不应为字符串。
|
||||
建议: 根据意图改写为 `const y = 'ab';` 或 `const y = 'a' + 'b';`。
|
||||
- 🟡 [AI] [bug] `exception-swallowed` L442
|
||||
**catch 参数被重新赋值,原始异常被静默吞掉**
|
||||
L442 的 `e = new Error('reassigned');` 只是改变 catch 局部变量 e 的引用,原始异常没有被记录也没有被重新抛出,这段 catch 块的语义很可能是错误的:异常在无人知晓的情况下被吞掉。
|
||||
建议: 如需记录并重新抛出,应使用 `console.error(e); throw new Error('reassigned', { cause: e });`;如果只是演示重抛,应该写 `throw e;`。
|
||||
@@ -0,0 +1,119 @@
|
||||
# demo-eslint 插件实测覆盖率报告
|
||||
|
||||
> **工程**:`demo-eslint`(纯内置配置形态:无 `eslint.config.*`)
|
||||
> **测试对象**:插件 ESLint 适配器(引擎 eslint 9.39.4 + @eslint/js 9.39.4 + typescript-eslint 8.60.1)
|
||||
> **实测日期**:2026-08-25
|
||||
> **比对基线**:插件同版本依赖复现的模拟基线(比对键 = 规则 + 行号 + 严重级别)
|
||||
|
||||
---
|
||||
|
||||
## 一、核心结论
|
||||
|
||||
| 指标 | 结果 | 状态 |
|
||||
|---|---|---|
|
||||
| 插件行为一致性(vs 模拟基线) | **292/292 条逐条一致,零偏差** | ✅ 100% |
|
||||
| 规则覆盖率(真实规则口径) | **126/126 种(100%)** | ✅ 全覆盖 |
|
||||
| 解析错误 | **0 处** | ✅ |
|
||||
| 诊断总数 | 292 条静态(error 268 / warning 24) | — |
|
||||
|
||||
**实测完全达成全覆盖基准**:插件在「工程无配置文件 → 回退内置默认配置」路径下,输出的 292 条静态诊断与模拟基线逐条一致,126 种内置规则全部触发、零解析错误。**ESLint 适配器静态分析链路(规则加载、解析、诊断映射)验证通过。**
|
||||
|
||||
---
|
||||
|
||||
## 二、逐文件比对(实测 vs 基线)
|
||||
|
||||
| 文件 | 实测 | 基线 | 一致性 | 说明 |
|
||||
|---|---:|---:|---|---|
|
||||
| `common.js` | 210 | 210 | ✅ 逐条一致 | core ESLint 规则主样例文件 |
|
||||
| `typescript.ts` | 65 | 65 | ✅ 逐条一致 | `@typescript-eslint/` 规则主样例文件 |
|
||||
| `legacysyntax.cjs` | 14 | 14 | ✅ 逐条一致 | 旧式(宽松模式)语法规则样例文件 |
|
||||
| `esm-demo.js` | 3 | 3 | ✅ 逐条一致 | ESM 相关规则样例文件 |
|
||||
| **合计** | **292** | **292** | **✅ 零偏差** | — |
|
||||
|
||||
> 比对方法:将插件报告中的每条静态诊断(规则名 + 行号 + 严重级别)与模拟基线做多重集合比对;四文件全部 `missing = 0`、`extra = 0`。
|
||||
|
||||
---
|
||||
|
||||
## 三、全覆盖的达成机制
|
||||
|
||||
126 种内置规则中,有 6 条规则无法在常规 `.js`(module 模式)样例中触发,通过 `legacysyntax.cjs` 单文件解决,机制如下:
|
||||
|
||||
1. **5 条宽松模式规则**(`no-nonoctal-decimal-escape`、`no-octal`、`no-with`、`no-delete-var`、`no-dupe-args`):触发代码(`'\8'` 转义、八进制 `010`、`with`、`delete` 变量、重复参数)在严格模式下本身是 SyntaxError,结构上只可能在宽松(sloppy)模式触发。`.cjs` 文件被 flat config 默认按 `sourceType: "commonjs"`(宽松模式)解析,无需任何配置文件。
|
||||
2. **`preserve-caught-error`**:eslint 9.39.4 引擎已实现该规则,但 @eslint/js 9.39.4 的 recommended 未启用(10.0.1 才收录)。通过文件首行 `/* eslint preserve-caught-error: "error" */` 行内注释启用。
|
||||
|
||||
### 6 条规则验证明细(`legacysyntax.cjs`)
|
||||
|
||||
| 规则 | 实测行号 | 严重级别 | 状态 |
|
||||
|---|---|---|---|
|
||||
| `no-nonoctal-decimal-escape` | L8(×2) | error | ✅ 触发 |
|
||||
| `no-octal` | L11 | error | ✅ 触发 |
|
||||
| `no-with` | L16 | error | ✅ 触发 |
|
||||
| `no-delete-var` | L22 | error | ✅ 触发 |
|
||||
| `no-dupe-args` | L25 | error | ✅ 触发 |
|
||||
| `preserve-caught-error` | L33 | error | ✅ 触发(行内注释) |
|
||||
|
||||
同文件连带触发的其他规则(与基线一致):`no-undef` L17(×3)/ L36、`no-var` L21、`no-redeclare` L25、`no-unused-vars` L32。
|
||||
|
||||
---
|
||||
|
||||
## 四、诊断分布统计
|
||||
|
||||
**按严重级别**(静态 292 条):
|
||||
|
||||
| 级别 | 数量 | 占比 |
|
||||
|---|---:|---:|
|
||||
| error(🔴) | 268 | 91.8% |
|
||||
| warning(🟡) | 24 | 8.2% |
|
||||
|
||||
**按规则类别**(126 种):
|
||||
|
||||
| 类别 | 种数 | 说明 |
|
||||
|---|---:|---|
|
||||
| core ESLint 规则 | 94 | @eslint/js 9.39.4 recommended + 插件精选规则 |
|
||||
| `@typescript-eslint/` 规则 | 32 | ts.configs.recommended + 插件精选规则 |
|
||||
|
||||
**按文件**:`common.js` 210 · `typescript.ts` 65 · `legacysyntax.cjs` 14 · `esm-demo.js` 3。
|
||||
|
||||
> 注:插件报告中另有 AI 审查建议 12 条(common.js 4 / legacysyntax.cjs 4 / esm-demo.js 2 / typescript.ts 2),属 AI 增强能力,不计入静态规则覆盖口径。
|
||||
|
||||
---
|
||||
|
||||
## 五、实测环境判定
|
||||
|
||||
实测报告的文件路径与预期部署一致:
|
||||
|
||||
- `data\demo-eslint\src\legacysyntax.cjs`(14 条诊断、无 parse-error → **`.cjs` 宽松模式解析生效,确认插件未发现任何工程配置、正确回退内置配置**)
|
||||
- `data\demo-eslint\src\`(common.js / esm-demo.js / typescript.ts,诊断数与基线完全相同)
|
||||
|
||||
**配置路径验证**:工程不含任何 `eslint.config.*` 与 `.eslintrc*`,插件按设计走「内置默认配置回退」路径。该路径与工作区打开方式无关(打开工程根或外层目录结果一致),292/292 逐条一致即为最终确认。
|
||||
|
||||
---
|
||||
|
||||
## 六、数据口径说明
|
||||
|
||||
| 项 | 说明 |
|
||||
|---|---|
|
||||
| 覆盖率口径 | 126 种 = 插件内置配置实际生效 125 种(@eslint/js 9.39.4 recommended + 精选规则)+ `preserve-caught-error`(由 `.cjs` 首行行内注释启用);纯规则口径,不含 parse-error |
|
||||
| 比对引擎 | eslint 9.39.4 + @eslint/js 9.39.4 + typescript-eslint 8.60.1(与插件 `package-lock.json` 逐字一致) |
|
||||
| 基线数据 | `/data/user/work/results/round2-verify.json`(本轮比对明细) |
|
||||
| 已知口径差异 | 工程自检脚本 `run-coverage.mjs`(用工程自带 @eslint/js 10.0.1)计 294 条:多出的 1 条为 `typescript.ts` L189 `preserve-caught-error`(10.0.1 的 recommended 启用该规则、插件 9.39.4 内置配置未启用),属引擎版本口径差异,非插件缺陷 |
|
||||
| 版本备注 | 测试机 `typescript.ts` 为 202 行版本(65 条);当前版(212 行)多 1 条 `@typescript-eslint/no-unused-vars` L188,同步与否均不影响 126 种全覆盖结论 |
|
||||
|
||||
---
|
||||
|
||||
## 七、结论与后续
|
||||
|
||||
**demo-eslint 工程的插件实测验证完成**:
|
||||
|
||||
1. ✅ 插件行为零偏差(292/292 逐条一致)
|
||||
2. ✅ 内置配置全覆盖达成(126/126 种规则,零解析错误)
|
||||
3. ✅ 「内置配置回退」路径完整验证(工程无配置文件的场景)
|
||||
|
||||
**后续建议**(如需继续扩展验证):
|
||||
|
||||
- **其他 3 个工程复测**:`demo-sqlfluff`、`demo-pmd`、`demo-stylelint` 按同样方法(先本地模拟基线 → 实测比对),其中 PMD 注意 Java 版本与 ruleset、SQLFluff 注意 dialect 与 AL06 样例、Stylelint 注意配置优先级。
|
||||
- **「工程配置优先」路径补验**(可选):若后续想验证插件发现工程配置的行为,可在工程根添加 `eslint.config.mjs` 并以工程根为第一个工作区文件夹打开,预期插件改用工程配置(诊断会有差异,属正常)。
|
||||
|
||||
---
|
||||
|
||||
*报告生成于 2026-08-25 · demo-eslint 插件实测 · 比对脚本:`round2-compare.mjs` · 数据:`round2-verify.json`*
|
||||
@@ -0,0 +1,33 @@
|
||||
# 代码审查报告
|
||||
|
||||
**文件:** `data\demo-eslint\src\esm-demo.js`
|
||||
**语言:** javascript
|
||||
**耗时:** 64.7s
|
||||
**分析工具:** eslint
|
||||
|
||||
---
|
||||
|
||||
总计: 5 | 错误: 3 | 警告: 1 | 建议: 1
|
||||
|
||||
静态分析 · 3 个问题
|
||||
|
||||
- 🔴 `eslint:no-unused-vars` L7
|
||||
第7行的 'reassignImport' 已定义但从未使用。
|
||||
建议: 该函数未被调用,应删除;如果后续可能使用,请在代码中调用它。建议直接删除整个函数,避免死代码。
|
||||
- 🔴 `eslint:no-import-assign` L8
|
||||
第8行 'fs' 是只读的,不能为其赋值。
|
||||
建议: import 导入的绑定是只读的,重新赋值会引发运行时错误。如果函数需要操作 fs,请使用另一个变量保存导入的对象;或删除该赋值语句。由于函数本身未使用,建议删除整个函数。
|
||||
- 🔴 `eslint:no-undef` L11
|
||||
第11行 'console' 未定义。
|
||||
建议: console 在浏览器和 Node.js 中通常是全局对象,但 ESLint 环境未配置。请在 .eslintrc 中设置 env.node 或 env.browser,或在代码顶部添加 /* global console */ 注释。或者改用 globalThis.console.log(fs)。
|
||||
|
||||
AI 审查 · 2 条建议
|
||||
|
||||
- 🟡 [AI] [design] `dead-code-with-runtime-error` L7
|
||||
**死代码中隐藏运行时错误**
|
||||
reassignImport 函数为 import 绑定 fs 赋值,在 ESM 中这是非法操作,一旦调用会立即抛出 TypeError。虽然当前函数未使用,但保留这种死代码会误导后续维护者,并可能被误调用导致崩溃。
|
||||
建议: 删除整个函数 reassignImport,或将其重构为合法操作(例如读取 fs 的属性)。
|
||||
- 🔵 [AI] [design] `module-top-level-side-effect` L11
|
||||
**模块顶层执行 console.log 产生副作用**
|
||||
第 11 行的 console.log 在模块被导入时立即执行。如果这是被复用的模块,会导致控制台输出,污染调用方,且难以测试。
|
||||
建议: 将 console.log 移到函数内部或通过导出接口提供;若仅是调试用途,在提交前移除。
|
||||
@@ -0,0 +1,74 @@
|
||||
# 代码审查报告
|
||||
|
||||
**文件:** `data\demo-eslint\src\legacysyntax.cjs`
|
||||
**语言:** javascript
|
||||
**耗时:** 152.1s
|
||||
**分析工具:** eslint
|
||||
|
||||
---
|
||||
|
||||
总计: 18 | 错误: 14 | 警告: 2 | 建议: 2
|
||||
|
||||
静态分析 · 14 个问题
|
||||
|
||||
- 🔴 `eslint:no-nonoctal-decimal-escape` L8
|
||||
不要使用 '\8' 转义序列。
|
||||
建议: 在非严格模式字符串中 `\8` 会被静默当作普通字符 `8`。如果只是要字符 8,请直接写 `'8 and 9'`;如果要表示反斜杠加数字,请使用 `String.raw` 或双反斜杠转义。
|
||||
- 🔴 `eslint:no-nonoctal-decimal-escape` L8
|
||||
不要使用 '\9' 转义序列。
|
||||
建议: 在非严格模式字符串中 `\9` 会被静默当作普通字符 `9`。如果只是要字符 9,请直接写 `'8 and 9'`;如果要表示反斜杠加数字,请使用 `String.raw` 或双反斜杠转义。
|
||||
- 🔴 `eslint:no-octal` L11
|
||||
不应使用旧式八进制字面量。
|
||||
建议: `010` 是旧式八进制字面量,值为十进制 8,且在严格模式下是语法错误。若要表示八进制值 8,应写 `0o10`;若要表示十进制 10,应写 `10`。
|
||||
- 🔴 `eslint:no-with` L16
|
||||
不应使用 `with` 语句。
|
||||
建议: `with` 会动态修改作用域链,难以静态分析和优化,在严格模式或 ESM 中还是语法错误。建议先解构出所需属性:`const { pi, e } = { pi: 3.14, e: 2.72 };` 再使用。
|
||||
- 🔴 `eslint:no-undef` L17
|
||||
未定义变量 `console`。
|
||||
建议: ESLint 配置中没有声明 `console` 全局变量。建议在 `eslint.config.mjs` 中设置 `languageOptions.globals` 为 `globals.node`,或在本文件中使用 `globalThis.console`。
|
||||
- 🔴 `eslint:no-undef` L17
|
||||
未定义变量 `pi`。
|
||||
建议: `with` 对象中的属性 `pi` 不会成为真正的词法作用域变量,因此静态分析无法识别。请参考 `no-with` 的修复:先解构出 `pi` 再使用。
|
||||
- 🔴 `eslint:no-undef` L17
|
||||
未定义变量 `e`。
|
||||
建议: `with` 对象中的属性 `e` 不会成为真正的词法作用域变量,因此静态分析无法识别。请参考 `no-with` 的修复:先解构出 `e` 再使用。
|
||||
- 🔴 `eslint:no-var` L21
|
||||
不应使用 `var`,请改用 `let` 或 `const`。
|
||||
建议: `deletable` 后续会被置空,使用 `let deletable = 42;`;如果确定不再重新赋值,可考虑 `const`。
|
||||
- 🔴 `eslint:no-delete-var` L22
|
||||
不应删除变量。
|
||||
建议: `delete` 只能删除对象属性,不能移除 `var`/`let`/`const` 声明的变量绑定。若要清理引用,请改为 `deletable = null;`;若要真正可删除,请把数据放进对象属性再 `delete`。
|
||||
- 🔴 `eslint:no-dupe-args` L25
|
||||
函数参数名重复:`a`。
|
||||
建议: 重命名第二个参数,并保持函数行为不变:`function duplicateArgs(a, b) { return b; }`。
|
||||
- 🔴 `eslint:no-redeclare` L25
|
||||
`a` 已重复声明。
|
||||
建议: 移除重复参数名,将第二个参数改名为 `b`,并返回 `b`。
|
||||
- 🔴 `eslint:no-unused-vars` L32
|
||||
变量 `err` 已被声明但从未使用。
|
||||
建议: 如果要保留原始错误,请把它作为 `cause` 传给新错误:`throw new Error('parse failed', { cause: err });`;如果确实不需要原始错误,可使用省略绑定参数的 `catch { ... }`。
|
||||
- 🔴 `eslint:preserve-caught-error` L33
|
||||
抛出的新错误没有附带原始错误作为 `cause`。
|
||||
建议: 在抛出 `Error` 时使用第二个 options 参数传递 `cause`:`throw new Error('parse failed', { cause: err });`,以保留原始错误链和堆栈信息。
|
||||
- 🔴 `eslint:no-undef` L36
|
||||
未定义变量 `console`。
|
||||
建议: 在 ESLint 配置中声明 `console` 全局变量,或使用 `globalThis.console.log(...)`。
|
||||
|
||||
AI 审查 · 4 条建议
|
||||
|
||||
- 🟡 [AI] [bug] `legacy-octal-value-confusion` L11
|
||||
**旧式八进制字面量 `010` 的实际值是 8,而非 10**
|
||||
在宽松模式下,`010` 会被解析为旧式八进制字面量,等于十进制 8。如果写 `010` 的人以为是十进制 10,就会引入静默的数值错误;即使确实想表示八进制,也应使用明确的 `0o10`,避免歧义并绕过 `no-octal` 规则。
|
||||
建议: 若语义是十进制 10,改为 `10`;若语义是八进制 8,改为 `0o10`。
|
||||
- 🟡 [AI] [bug] `delete-var-no-op` L22
|
||||
**`delete deletable` 不会删除变量,清理意图不生效**
|
||||
`delete` 操作符只对对象属性有意义;对 `var` 声明的标识符执行 `delete` 不会移除该绑定,在非严格模式下通常静默失败并返回 `false`,`deletable` 仍然是 42。这样的代码会给维护者造成“变量已被删除”的错觉。
|
||||
建议: 如果只想清空引用,改为 `deletable = null;`;如果需要“可删除”的数据,请改为 `const data = { deletable: 42 };` 然后 `delete data.deletable;`。
|
||||
- 🔵 [AI] [design] `legacy-syntax-in-production-fixture` L3
|
||||
**演示旧式语法的代码不应混入生产/业务源码**
|
||||
该文件故意依靠 `.cjs` 和 sloppy mode 来使用 `with`、旧式八进制、重复参数等旧式语法。一旦文件被加入 `"use strict"`,或迁移为 ESM/被复制进严格模式环境,这些代码会立即变成 `SyntaxError`。作为语法规则演示/回归样例,应放在测试 fixtures 中并由 ESLint 显式配置解析。
|
||||
建议: 将这类 legacy 语法样例移动到 `tests/fixtures/legacy/`,在测试配置中明确 `sourceType: 'script'` 和适用的 `ecmaVersion`,不要放在主代码目录。
|
||||
- 🔵 [AI] [design] `missing-node-globals-config` L17
|
||||
**ESLint 未声明 Node 全局变量,导致 `console` 反复触发 `no-undef`**
|
||||
第 17、36 行的 `console` 在 Node 运行时是合法的全局变量,但当前 ESLint 配置没有引入 `globals.node` 或文件级注释,所以被重复报告为未定义。逐个改成 `globalThis.console` 会掩盖根因:ESLint 环境配置缺失。
|
||||
建议: 在 `eslint.config.mjs` 中 `languageOptions.globals` 引入 `globals.node`;或在本文件顶部增加 `/* global console */`。
|
||||
@@ -0,0 +1,219 @@
|
||||
# 代码审查报告
|
||||
|
||||
**文件:** `data\demo-eslint\src\typescript.ts`
|
||||
**语言:** typescript
|
||||
**耗时:** 257.3s
|
||||
**分析工具:** eslint
|
||||
|
||||
---
|
||||
|
||||
总计: 67 | 错误: 57 | 警告: 10 | 建议: 0
|
||||
|
||||
静态分析 · 65 个问题
|
||||
|
||||
- 🔴 `eslint:@typescript-eslint/triple-slash-reference` L5
|
||||
不要对 ./typesmodule.ts 使用三斜线引用,请改用 import 风格。
|
||||
建议: 删除这一行;若必须引入副作用,使用 `import './typesmodule';`。
|
||||
- 🔴 `eslint:@typescript-eslint/consistent-type-imports` L8
|
||||
声明中的所有导入都只被用作类型,请使用 `import type`。
|
||||
建议: 将第 8 行改为 `import type { DemoType } from './typesmodule';`。
|
||||
- 🔴 `eslint:@typescript-eslint/no-unused-vars` L11
|
||||
'legacyLoad' 已定义但从未被使用。
|
||||
建议: 删除该函数,或将其加入底部 export 列表 / 添加 `export` 前缀。
|
||||
- 🔴 `eslint:no-var` L12
|
||||
检测到 var,建议使用 let 或 const。
|
||||
建议: 将 `var mod` 改为 `const mod`,并考虑用 ES import 替代 require。
|
||||
- 🔴 `eslint:@typescript-eslint/no-require-imports` L12
|
||||
禁止使用 require 风格导入。
|
||||
建议: 移除 require,改用 ES module:在文件顶部 `import * as mod from './typesmodule';`,并在函数中返回 `mod`。
|
||||
- 🔴 `eslint:@typescript-eslint/no-unused-vars` L17
|
||||
'arrayConstructor' 已定义但从未被使用。
|
||||
建议: 删除函数,或添加 `export` 前缀导出。
|
||||
- 🔴 `eslint:@typescript-eslint/no-array-constructor` L18
|
||||
建议优先使用数组字面量 []。
|
||||
建议: 用 `[1, 2, 3]` 代替 `new Array(1, 2, 3)`。
|
||||
- 🔴 `eslint:@typescript-eslint/no-dynamic-delete` L23
|
||||
不要动态删除计算属性键。
|
||||
建议: 改用 `Map<string, unknown>` 并通过 `map.delete(key)` 删除;如果必须使用对象,只能删除字面量属性或使用 `Reflect.deleteProperty` 并明确其风险。
|
||||
- 🔴 `eslint:@typescript-eslint/no-unused-vars` L27
|
||||
'UtilityClass' 已定义但从未被使用。
|
||||
建议: 导出该类或删除它。
|
||||
- 🔴 `eslint:@typescript-eslint/no-unused-vars` L34
|
||||
'invalidVoid' 已定义但从未被使用。
|
||||
建议: 删除函数或添加 `export` 前缀。
|
||||
- 🔴 `eslint:@typescript-eslint/no-non-null-assertion` L41
|
||||
禁止非空断言。
|
||||
建议: 删除 `!`,让 null/undefined 交还给类型系统处理。
|
||||
- 🔴 `eslint:@typescript-eslint/no-non-null-asserted-nullish-coalescing` L41
|
||||
空值合并运算符本身就是为处理 null/undefined 设计的,使用非空断言没有意义。
|
||||
建议: 直接写 `return value ?? 'fallback';`。
|
||||
- 🔴 `eslint:@typescript-eslint/no-non-null-assertion` L46
|
||||
禁止非空断言。
|
||||
建议: 不要在 `value` 上使用 `!`;如果值为 null,请先判空。
|
||||
- 🔴 `eslint:@typescript-eslint/no-non-null-assertion` L46
|
||||
禁止非空断言。
|
||||
建议: 去掉非空断言并做空值保护。
|
||||
- 🔴 `eslint:@typescript-eslint/no-extra-non-null-assertion` L46
|
||||
禁止多余的非空断言。
|
||||
建议: 至多保留一个 `!`(最好不用),并改为 `value?.length ?? 0`。
|
||||
- 🔴 `eslint:@typescript-eslint/no-unused-vars` L50
|
||||
'optionalChainAsserted' 已定义但从未被使用。
|
||||
建议: 删除函数或添加 `export` 前缀。
|
||||
- 🔴 `eslint:@typescript-eslint/no-non-null-assertion` L51
|
||||
禁止非空断言。
|
||||
建议: 移除可选链后的 `!`,改用 `??` 提供默认值。
|
||||
- 🔴 `eslint:@typescript-eslint/no-non-null-asserted-optional-chain` L51
|
||||
可选链表达式本来就可能返回 undefined,使用非空断言既不安全也错误。
|
||||
建议: 删除 `!`,通过 `??` 或条件判断处理 undefined。
|
||||
- 🔴 `eslint:@typescript-eslint/no-unused-vars` L55
|
||||
'thisAlias' 已定义但从未被使用。
|
||||
建议: 删除函数或添加 `export` 前缀。
|
||||
- 🔴 `eslint:@typescript-eslint/no-this-alias` L56
|
||||
不允许将 this 赋值给局部变量。
|
||||
建议: 直接访问 `this.x`;若在嵌套函数中需要 this,改用箭头函数或显式传入上下文对象。
|
||||
- 🔴 `eslint:@typescript-eslint/no-unused-vars` L62
|
||||
'shadowed' 被赋值后从未使用。
|
||||
建议: 删除外层未使用的变量,或改成 `void shadowed;` 进行使用。
|
||||
- 🔴 `eslint:@typescript-eslint/no-shadow` L64
|
||||
'shadowed' 已在第 62 行第 9 列的上层作用域中声明。
|
||||
建议: 将内部变量重命名,避免遮蔽外层变量。
|
||||
- 🔴 `eslint:@typescript-eslint/no-unused-vars` L71
|
||||
'preferConstInTs' 已定义但从未被使用。
|
||||
建议: 删除函数或添加 `export` 前缀。
|
||||
- 🔴 `eslint:prefer-const` L72
|
||||
'fixed' 从未被重新赋值,建议使用 const。
|
||||
建议: 把 `let fixed` 改为 `const fixed`。
|
||||
- 🔴 `eslint:@typescript-eslint/no-unused-vars` L77
|
||||
'EmptyConstructor' 已定义但从未被使用。
|
||||
建议: 删除类或添加 `export` 前缀。
|
||||
- 🔴 `eslint:@typescript-eslint/no-useless-empty-export` L82
|
||||
空的 export 没有任何作用,可以删除。
|
||||
建议: 删除 `export {};`,因为文件已有其他导出。
|
||||
- 🔴 `eslint:@typescript-eslint/no-unused-vars` L85
|
||||
'UninitializedEnum' 已定义但从未被使用。
|
||||
建议: 删除枚举或添加 `export` 前缀。
|
||||
- 🔴 `eslint:@typescript-eslint/no-unused-vars` L89
|
||||
'DuplicateEnum' 已定义但从未被使用。
|
||||
建议: 删除枚举或添加 `export` 前缀。
|
||||
- 🔴 `eslint:@typescript-eslint/no-duplicate-enum-values` L91
|
||||
重复的枚举成员值 1。
|
||||
建议: 将 B 改为不同值,如 `B = 2`。
|
||||
- 🔴 `eslint:@typescript-eslint/no-unused-vars` L96
|
||||
'NonLiteralEnum' 已定义但从未被使用。
|
||||
建议: 删除枚举或添加 `export` 前缀。
|
||||
- 🔴 `eslint:@typescript-eslint/unified-signatures` L102
|
||||
这些重载可以合并成一个签名 `string | 'x'`。
|
||||
建议: 合并重载;由于 `'x'` 是 `string` 的子类型,直接删除第二个重载即可。
|
||||
- 🔴 `eslint:@typescript-eslint/no-explicit-any` L108
|
||||
意外的 any 类型,请指定具体类型。
|
||||
建议: 将参数类型改为泛型或 `unknown` 后收窄。
|
||||
- 🔴 `eslint:@typescript-eslint/no-explicit-any` L108
|
||||
意外的 any 类型,请指定具体类型。
|
||||
建议: 将返回值类型改为泛型或具体类型。
|
||||
- 🔴 `eslint:@typescript-eslint/no-empty-object-type` L113
|
||||
`{}` 空对象类型允许任意非空值,包括字面量 `0` 和 `""`。如需保留此行为,请通过行内注释禁用该规则或配置 allowObjectTypes;如需表示任意对象,应使用 `object`;如需表示任意值,应使用 `unknown`。
|
||||
建议: 将 `EmptyObject` 定义为 `type EmptyObject = object;` 或 `unknown`。
|
||||
- 🔴 `eslint:@typescript-eslint/no-empty-object-type` L114
|
||||
空接口声明允许任意非空值,包括字面量 `0` 和 `""`。如需保留此行为,请通过行内注释禁用该规则或配置 allowInterfaces;如需表示任意对象,应使用 `object`;如需表示任意值,应使用 `unknown`。
|
||||
建议: 将空接口改为 `type EmptyInterface = object;`(若想让任意对象类型)或 `unknown`。
|
||||
- 🔴 `eslint:@typescript-eslint/no-unused-vars` L114
|
||||
'EmptyInterface' 已定义但从未被使用。
|
||||
建议: 删除接口或添加 `export` 前缀(改为 `export type EmptyInterface = object;` 可同时解决空类型问题)。
|
||||
- 🔴 `eslint:@typescript-eslint/no-unused-vars` L115
|
||||
'emptyObj' 被赋值后从未使用。
|
||||
建议: 删除变量或添加 `export` 前缀,或使用 `void emptyObj;` 标记为有意使用。
|
||||
- 🔴 `eslint:@typescript-eslint/no-wrapper-object-types` L118
|
||||
建议使用原始类型 `string` 作为类型名,而不是大写包装类型 `String`。
|
||||
建议: 将参数类型 `String` 改为 `string`。
|
||||
- 🔴 `eslint:@typescript-eslint/no-wrapper-object-types` L118
|
||||
建议使用原始类型 `number` 作为类型名,而不是大写包装类型 `Number`。
|
||||
建议: 将参数类型 `Number` 改为 `number`。
|
||||
- 🔴 `eslint:@typescript-eslint/no-wrapper-object-types` L118
|
||||
建议使用原始类型 `boolean` 作为类型名,而不是大写包装类型 `Boolean`。
|
||||
建议: 将参数类型 `Boolean` 改为 `boolean`。
|
||||
- 🔴 `eslint:@typescript-eslint/no-unsafe-function-type` L123
|
||||
`Function` 类型可以接受任意函数式值,建议显式定义函数参数和返回类型。
|
||||
建议: 将 `UnsafeFn` 定义为明确的函数签名,如 `type UnsafeFn = (...args: unknown[]) => unknown;`。
|
||||
- 🔴 `eslint:@typescript-eslint/no-unused-expressions` L130
|
||||
期望赋值或函数调用,但看到的是表达式。
|
||||
建议: 删除无副作用的 `flag && value;`,或改成 `if (flag) { return value; }`。
|
||||
- 🔴 `eslint:@typescript-eslint/no-unused-expressions` L131
|
||||
期望赋值或函数调用,但看到的是表达式。
|
||||
建议: 删除 `value;` 语句,或改为 `return value;`。
|
||||
- 🔴 `eslint:@typescript-eslint/no-unnecessary-type-constraint` L135
|
||||
将泛型 `T` 约束为 `unknown` 没有任何作用,是不必要的。
|
||||
建议: 移除 `extends unknown` 约束,直接写成 `<T>`。
|
||||
- 🔴 `eslint:@typescript-eslint/no-unsafe-declaration-merging` L140
|
||||
类与接口之间的声明合并是不安全的。
|
||||
建议: 不要在类与接口间做同名合并;将成员直接定义在类中。
|
||||
- 🔴 `eslint:@typescript-eslint/no-unused-vars` L140
|
||||
'MergedThing' 已定义但从未被使用。
|
||||
建议: 删除未使用声明,或添加 `export` 前缀。
|
||||
- 🔴 `eslint:@typescript-eslint/no-unsafe-declaration-merging` L143
|
||||
类与接口之间的声明合并是不安全的。
|
||||
建议: 同上,消除同名合并,并在类中实现方法。
|
||||
- 🔴 `eslint:@typescript-eslint/no-unused-vars` L148
|
||||
'tsIgnored' 已定义但从未被使用。
|
||||
建议: 删除函数或添加 `export` 前缀。
|
||||
- 🔴 `eslint:@typescript-eslint/ban-ts-comment` L149
|
||||
请使用 `@ts-expect-error` 代替 `@ts-ignore`,因为 `@ts-ignore` 在下一行没有错误时不会报错。
|
||||
建议: 将 `@ts-ignore` 改为 `@ts-expect-error`,或修复下一行的类型错误后删除注释。
|
||||
- 🔴 `eslint:@typescript-eslint/no-unused-vars` L155
|
||||
'MisusedNew' 已定义但从未被使用。
|
||||
建议: 删除接口或添加 `export` 前缀。
|
||||
- 🔴 `eslint:@typescript-eslint/no-misused-new` L156
|
||||
接口不能被实例化,只有类可以。
|
||||
建议: 将构造签名的定义放到 `class` 中,而不是 `interface`。
|
||||
- 🔴 `eslint:@typescript-eslint/no-misused-new` L157
|
||||
接口不能被实例化,只有类可以。
|
||||
建议: 不要在接口中声明 `constructor`,改写为类的构造器。
|
||||
- 🔴 `eslint:@typescript-eslint/no-namespace` L161
|
||||
ES2015 模块语法优于命名空间。
|
||||
建议: 将 `namespace LegacyNamespace { ... }` 改为普通对象/ES module 导出。
|
||||
- 🔴 `eslint:@typescript-eslint/prefer-namespace-keyword` L164
|
||||
请使用 `namespace` 而不是 `module` 来声明自定义 TypeScript 模块。
|
||||
建议: 将 `module LegacyModule` 改为 `namespace LegacyModule`(但更推荐 ES module 语法)。
|
||||
- 🔴 `eslint:@typescript-eslint/no-namespace` L164
|
||||
ES2015 模块语法优于命名空间。
|
||||
建议: 将 `module LegacyModule` 改为普通对象/ES module 导出,避免模块命名空间。
|
||||
- 🔴 `eslint:@typescript-eslint/prefer-as-const` L170
|
||||
期望使用 const 断言,而不是字面量类型注解。
|
||||
建议: 改为 `const literal = 'hello' as const;`。
|
||||
- 🟡 `eslint:@typescript-eslint/no-extraneous-class` L27
|
||||
类中只有静态属性/方法,不应使用类。
|
||||
建议: 改用普通对象:`export const UtilityClass = { helper: (): number => 42 };`。
|
||||
- 🟡 `eslint:@typescript-eslint/no-invalid-void-type` L34
|
||||
void 不能作为联合类型的成员。
|
||||
建议: 移除联合中的 void,改为返回 `undefined` 或直接使用 `: void`;若函数有返回值,使用具体类型。
|
||||
- 🟡 `eslint:@typescript-eslint/no-extraneous-class` L77
|
||||
类中只有一个空的构造器,不应使用类。
|
||||
建议: 删除整个类;若需要实例化对象,写成普通对象或函数。
|
||||
- 🟡 `eslint:@typescript-eslint/no-useless-constructor` L78
|
||||
无用的构造器。
|
||||
建议: 直接移除 `constructor() {}`,让类使用默认构造器。
|
||||
- 🟡 `eslint:@typescript-eslint/prefer-enum-initializers` L86
|
||||
成员 'First' 的值应显式定义。
|
||||
建议: 显式赋值,如 `First = 0`。
|
||||
- 🟡 `eslint:@typescript-eslint/prefer-enum-initializers` L87
|
||||
成员 'Second' 的值应显式定义。
|
||||
建议: 显式赋值,如 `Second = 1`。
|
||||
- 🟡 `eslint:@typescript-eslint/prefer-literal-enum-member` L97
|
||||
显式枚举值只能是字符串或数字字面量。
|
||||
建议: 将 `A = someValue` 改为 `A = 10`。
|
||||
- 🟡 `eslint:prefer-rest-params` L176
|
||||
请使用剩余参数代替 `arguments`。
|
||||
建议: 改为 `function restParams(...args: unknown[]) { return args; }`。
|
||||
- 🟡 `eslint:prefer-spread` L181
|
||||
请使用展开运算符代替 `.apply()`。
|
||||
建议: 改为 `return mySpreadFn(...base);`。
|
||||
|
||||
AI 审查 · 2 条建议
|
||||
|
||||
- 🔴 [AI] [bug] `missing-method-in-declaration-merging` L143
|
||||
**接口与类合并后 method 缺少实现**
|
||||
MergedThing 接口通过声明合并为同名类实例类型增加了 method(): void 签名,但类中没有实现该方法。因此类型检查允许 new MergedThing().method(),运行时会抛出 method is not a function 的 TypeError。静态规则只报告了合并不安全,没有指出缺少实现的问题。
|
||||
建议: 删除 interface MergedThing,并将 method 的实现写到 class MergedThing 中;如果确实需要接口合并,也要在类中提供对应实现。
|
||||
- 🟡 [AI] [design] `any-return-from-require` L12
|
||||
**legacyLoad 隐式返回 any 类型**
|
||||
require('./typesmodule') 的返回值类型为 any,函数没有显式返回类型,因此 legacyLoad 的返回类型被推断为 any,调用方会丢失类型检查。该类型泄漏不在 no-require-imports 规则的直接范围内。
|
||||
建议: 改用 ES module 导入并声明返回类型,例如删除 var mod = require(...),在文件顶部使用 import * as mod from './typesmodule';,并让函数返回 typeof mod。
|
||||
@@ -0,0 +1,166 @@
|
||||
// ESLint 覆盖率测试 v3:复刻内置配置(与 ESLintAdapter.getDefaultConfig 逐字一致)
|
||||
// + legacysyntax.js script 模式(对应 demo 工程配置 eslint.config.mjs)
|
||||
// + 以 calculateConfigForFile 计算各文件类型实际生效(severity != off)的规则集
|
||||
import { ESLint } from 'eslint';
|
||||
import js from '@eslint/js';
|
||||
import ts from 'typescript-eslint';
|
||||
import { readFileSync, readdirSync, writeFileSync } from 'fs';
|
||||
import { join, dirname } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const SRC = join(__dirname, 'src');
|
||||
|
||||
// ===== 以下与 src/rules/builtin-rules.ts 逐字一致 =====
|
||||
const eslintExtraRules = {
|
||||
'eqeqeq': 'error', 'no-eq-null': 'error', 'no-self-compare': 'error',
|
||||
'no-promise-executor-return': 'error', 'no-shadow': 'error', 'no-unassigned-vars': 'error',
|
||||
'no-useless-assignment': 'error', 'block-scoped-var': 'error', 'default-case': 'error',
|
||||
'default-case-last': 'error', 'no-unmodified-loop-condition': 'error', 'no-unreachable-loop': 'error',
|
||||
'no-eval': 'error', 'no-extend-native': 'error', 'no-var': 'error',
|
||||
'no-await-in-loop': 'warn', 'prefer-template': 'warn', 'prefer-object-spread': 'warn',
|
||||
'prefer-rest-params': 'warn', 'prefer-spread': 'warn', 'prefer-object-has-own': 'warn',
|
||||
'no-useless-concat': 'warn', 'no-useless-return': 'warn', 'no-useless-computed-key': 'warn',
|
||||
'no-useless-rename': 'warn', 'no-param-reassign': 'warn', 'no-return-assign': 'error',
|
||||
'no-throw-literal': 'error', 'camelcase': 'warn', 'new-cap': 'warn', 'no-array-constructor': 'error',
|
||||
};
|
||||
|
||||
const eslintExtraTsRules = {
|
||||
'@typescript-eslint/no-non-null-assertion': 'error',
|
||||
'@typescript-eslint/no-dynamic-delete': 'error',
|
||||
'@typescript-eslint/no-useless-empty-export': 'error',
|
||||
'@typescript-eslint/consistent-type-imports': 'error',
|
||||
'@typescript-eslint/unified-signatures': 'error',
|
||||
'@typescript-eslint/no-extraneous-class': 'warn',
|
||||
'@typescript-eslint/no-useless-constructor': 'warn',
|
||||
'@typescript-eslint/no-non-null-asserted-nullish-coalescing': 'error',
|
||||
'@typescript-eslint/no-invalid-void-type': 'warn',
|
||||
'@typescript-eslint/prefer-literal-enum-member': 'warn',
|
||||
'@typescript-eslint/prefer-enum-initializers': 'warn',
|
||||
'no-shadow': 'off',
|
||||
'@typescript-eslint/no-shadow': 'error',
|
||||
'no-array-constructor': 'off',
|
||||
};
|
||||
|
||||
const TS_FILES = ['**/*.ts', '**/*.tsx', '**/*.mts', '**/*.cts'];
|
||||
const JS_FILES = ['**/*.js', '**/*.jsx', '**/*.mjs', '**/*.cjs'];
|
||||
|
||||
const tsConfigs = ts.configs.recommended.map(cfg => ({
|
||||
...cfg,
|
||||
files: (cfg).files ?? TS_FILES,
|
||||
}));
|
||||
|
||||
// 复刻内置默认配置(与 ESLintAdapter.getDefaultConfig 一致)+ legacysyntax script 模式
|
||||
const defaultConfig = [
|
||||
js.configs.recommended,
|
||||
{ files: JS_FILES, languageOptions: { parserOptions: { ecmaFeatures: { jsx: true } } } },
|
||||
...tsConfigs,
|
||||
{ rules: eslintExtraRules },
|
||||
{ files: TS_FILES, rules: eslintExtraTsRules },
|
||||
{ files: ['**/legacysyntax.js'], languageOptions: { sourceType: 'script' } },
|
||||
];
|
||||
|
||||
const files = readdirSync(SRC).filter(f => /\.(js|ts|mjs|cjs)$/.test(f));
|
||||
const engine = new ESLint({ cwd: SRC, overrideConfigFile: true, overrideConfig: defaultConfig });
|
||||
|
||||
// ===== 实际生效规则(severity != off)=====
|
||||
async function effectiveRules(sampleFile) {
|
||||
const cfg = await engine.calculateConfigForFile(join(SRC, sampleFile));
|
||||
const out = new Set();
|
||||
for (const [name, val] of Object.entries(cfg.rules ?? {})) {
|
||||
const sev = Array.isArray(val) ? val[0] : val;
|
||||
if (sev !== 'off' && sev !== 0) out.add(name);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
const jsEnabled = await effectiveRules('common.js');
|
||||
const tsEnabled = await effectiveRules('typescript.ts');
|
||||
const builtinAll = new Set([...jsEnabled, ...tsEnabled]);
|
||||
|
||||
// ===== 逐文件 lint =====
|
||||
const actualByFile = {};
|
||||
const parseErrors = {};
|
||||
let violationTotal = 0;
|
||||
for (const f of files) {
|
||||
const text = readFileSync(join(SRC, f), 'utf8');
|
||||
const results = await engine.lintText(text, { filePath: join(SRC, f) });
|
||||
const rulesHit = new Set();
|
||||
for (const r of results) {
|
||||
for (const m of r.messages) {
|
||||
if (m.fatal) { parseErrors[f] = m.message; continue; }
|
||||
violationTotal++;
|
||||
if (m.ruleId) rulesHit.add(m.ruleId);
|
||||
}
|
||||
}
|
||||
actualByFile[f] = rulesHit;
|
||||
}
|
||||
|
||||
const actualRaw = new Set();
|
||||
for (const s of Object.values(actualByFile)) for (const r of s) actualRaw.add(r);
|
||||
|
||||
// ===== 期望规则提取(demo 注释标注)=====
|
||||
const NOISE = new Set(['eslint', '本文件', '说明', '保留', '合并', '用于', '异步', '比较', '该文件', 'eslint.config.mjs', '顺手在此以', '文件', '演示', '规则',
|
||||
// 注释噪音:文件名 / 指令 / 未启用规则名 / 笔误(真实规则为 no-unused-vars)
|
||||
'esm-demo', 'ts-ignore', 'no-empty-function', 'no-useless-vars']);
|
||||
const expectByFile = {};
|
||||
for (const f of files) {
|
||||
const text = readFileSync(join(SRC, f), 'utf8');
|
||||
const isTs = /\.ts$/.test(f);
|
||||
const expects = new Set();
|
||||
for (const line of text.split('\n')) {
|
||||
const m = line.match(/^\s*\/\/\s*(.+)$/);
|
||||
if (!m) continue;
|
||||
const body = m[1];
|
||||
// 候选 token:kebab-case 规则名 或 @typescript-eslint/ 前缀规则名
|
||||
for (const tm of body.matchAll(/(@typescript-eslint\/[a-z0-9-]+|[a-z][a-z0-9]*(-[a-z0-9]+)+)/g)) {
|
||||
const name = tm[1];
|
||||
if (NOISE.has(name)) continue;
|
||||
if (!isTs && name.startsWith('@typescript-eslint/')) continue;
|
||||
expects.add(name);
|
||||
}
|
||||
}
|
||||
expectByFile[f] = expects;
|
||||
}
|
||||
const expectAll = new Set();
|
||||
for (const s of Object.values(expectByFile)) for (const r of s) expectAll.add(r);
|
||||
|
||||
// ===== 统计 =====
|
||||
// 名称归一化:注释里通常省略 @typescript-eslint/ 前缀,比较时两边都剥掉前缀
|
||||
const normalize = r => r.replace(/^@typescript-eslint\//, '');
|
||||
const actualNorm = new Set([...actualRaw].map(normalize));
|
||||
const jsHit = [...jsEnabled].filter(r => actualRaw.has(r));
|
||||
const tsHit = [...tsEnabled].filter(r => actualRaw.has(r));
|
||||
const hitInBuiltin = [...builtinAll].filter(r => actualRaw.has(r));
|
||||
const expectHit = [...expectAll].filter(r => actualNorm.has(normalize(r)));
|
||||
const expectMissed = [...expectAll].filter(r => !actualNorm.has(normalize(r))).sort();
|
||||
|
||||
console.log('=== ESLint 覆盖率实测结果(v3,生效口径)===');
|
||||
console.log(`测试文件: ${files.join(', ')}`);
|
||||
console.log(`解析错误: ${Object.keys(parseErrors).length ? JSON.stringify(parseErrors) : '无'}`);
|
||||
console.log(`JS 生效规则: ${jsEnabled.size} 条,命中 ${jsHit.length} = ${(jsHit.length / jsEnabled.size * 100).toFixed(1)}%`);
|
||||
console.log(`TS 生效规则: ${tsEnabled.size} 条,命中 ${tsHit.length} = ${(tsHit.length / tsEnabled.size * 100).toFixed(1)}%`);
|
||||
console.log(`合并生效规则: ${builtinAll.size} 条,命中 ${hitInBuiltin.length} = ${(hitInBuiltin.length / builtinAll.size * 100).toFixed(1)}%`);
|
||||
console.log(`demo 期望规则: ${expectAll.size} 条,命中 ${expectHit.length}`);
|
||||
console.log(`违规总数: ${violationTotal}`);
|
||||
console.log(`\nJS 未命中 (${jsEnabled.size - jsHit.length}): ${[...jsEnabled].filter(r => !actualRaw.has(r)).sort().join(', ') || '无'}`);
|
||||
console.log(`\nTS 未命中 (${tsEnabled.size - tsHit.length}): ${[...tsEnabled].filter(r => !actualRaw.has(r)).sort().join(', ') || '无'}`);
|
||||
console.log(`\n期望未达成: ${expectMissed.join(', ') || '无'}`);
|
||||
|
||||
writeFileSync('/data/user/work/results/eslint_result.json', JSON.stringify({
|
||||
tool: 'eslint',
|
||||
builtinJsTotal: jsEnabled.size,
|
||||
builtinTsTotal: tsEnabled.size,
|
||||
builtinAllTotal: builtinAll.size,
|
||||
jsHit: jsHit.sort(),
|
||||
tsHit: tsHit.sort(),
|
||||
jsMissed: [...jsEnabled].filter(r => !actualRaw.has(r)).sort(),
|
||||
tsMissed: [...tsEnabled].filter(r => !actualRaw.has(r)).sort(),
|
||||
actualHitRaw: [...actualRaw].sort(),
|
||||
expectTotal: expectAll.size,
|
||||
expectHit: expectHit.sort(),
|
||||
expectMissed,
|
||||
violationTotal,
|
||||
parseErrors,
|
||||
}, null, 2));
|
||||
console.log('\n结果已写入 /data/user/work/results/eslint_result.json');
|
||||
@@ -0,0 +1,608 @@
|
||||
// ============ common.js(合并文件) ============
|
||||
// 由 ESLint 演示工程原 10 个 JS 文件合并压缩而来,用于触发各条 ESLint 规则。
|
||||
// 保留独立文件:legacysyntax.js(script 模式解析)、typescript.ts / typesmodule.ts(TS 规则)。
|
||||
// 注:本文件为普通 script(无 import/export),且不含严格模式下的致命语法样例
|
||||
//(with / delete 局部变量 / 重复形参 / 顶层重复 let 等),确保 ESLint 能完整解析并触发全部规则。
|
||||
|
||||
// ===== 原 async.js =====
|
||||
// ============ async.js ============
|
||||
// 异步 / Promise 相关规则
|
||||
|
||||
// require-yield
|
||||
function* generatorWithoutYield() {
|
||||
console.log('no yield here');
|
||||
}
|
||||
|
||||
// no-async-promise-executor
|
||||
const asyncExecutor = new Promise(async (resolve, reject) => {
|
||||
resolve(1);
|
||||
});
|
||||
|
||||
// no-promise-executor-return
|
||||
const executorReturn = new Promise((resolve, reject) => {
|
||||
return resolve(1);
|
||||
});
|
||||
|
||||
// no-await-in-loop
|
||||
async function awaitInLoop(items, url) {
|
||||
const results = [];
|
||||
for (const item of items) {
|
||||
const res = await fetch(url + item);
|
||||
results.push(res);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
// no-return-assign
|
||||
function returnAssign(x) {
|
||||
let assignedInReturn;
|
||||
return assignedInReturn = x;
|
||||
}
|
||||
|
||||
// no-throw-literal
|
||||
function throwLiteral() {
|
||||
throw 'not an error object';
|
||||
}
|
||||
|
||||
// no-useless-return
|
||||
function uselessReturn() {
|
||||
doSomething();
|
||||
return;
|
||||
}
|
||||
|
||||
function doSomething() {
|
||||
console.log('work');
|
||||
}
|
||||
|
||||
// ===== 原 classes.js =====
|
||||
// ============ classes.js ============
|
||||
// 类相关规则
|
||||
|
||||
// constructor-super
|
||||
class BadConstructor extends Error {
|
||||
constructor(message) {
|
||||
console.log(message);
|
||||
}
|
||||
}
|
||||
|
||||
// no-class-assign
|
||||
class Widget {}
|
||||
Widget = 'overwritten';
|
||||
|
||||
// no-dupe-class-members
|
||||
class DupeMember {
|
||||
method() {
|
||||
return 1;
|
||||
}
|
||||
method() {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
// no-empty-static-block
|
||||
class EmptyStatic {
|
||||
static {}
|
||||
}
|
||||
|
||||
// no-setter-return
|
||||
class SetterReturn {
|
||||
set value(v) {
|
||||
this._value = v;
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
// getter-return
|
||||
class GetterNoReturn {
|
||||
get value() {
|
||||
const x = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// no-this-before-super
|
||||
class ThisBeforeSuper extends Error {
|
||||
constructor(message) {
|
||||
this.message = message;
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
// no-unused-private-class-members
|
||||
class UnusedPrivate {
|
||||
#secret = 42;
|
||||
reveal() {
|
||||
return 'no secret';
|
||||
}
|
||||
}
|
||||
|
||||
// ===== 原 comparison.js =====
|
||||
// ============ comparison.js ============
|
||||
// 比较运算类规则
|
||||
|
||||
// eqeqeq
|
||||
function looseEquality(a, b) {
|
||||
return a == b;
|
||||
}
|
||||
|
||||
// no-eq-null
|
||||
function nullEquality(x) {
|
||||
return x == null;
|
||||
}
|
||||
|
||||
// no-self-compare
|
||||
function selfCompare(x) {
|
||||
return x === x;
|
||||
}
|
||||
|
||||
// no-compare-neg-zero
|
||||
function negZero(x) {
|
||||
return x === -0;
|
||||
}
|
||||
|
||||
// use-isnan
|
||||
function isNanWrong(x) {
|
||||
return x === NaN;
|
||||
}
|
||||
|
||||
// valid-typeof
|
||||
function typeofCheck(x) {
|
||||
return typeof x === 'strnig';
|
||||
}
|
||||
|
||||
// no-constant-binary-expression
|
||||
function constBinary() {
|
||||
const y = 'a' + -'b';
|
||||
return y;
|
||||
}
|
||||
|
||||
// no-unexpected-multiline already in syntax
|
||||
|
||||
// no-constant-condition already in syntax
|
||||
|
||||
// no-unsafe-negation already in syntax
|
||||
|
||||
// no-constant-binary-expression already covered
|
||||
|
||||
// ===== 原 control.js =====
|
||||
// ============ control.js ============
|
||||
// 流程控制 / switch 相关规则
|
||||
|
||||
// no-fallthrough
|
||||
function fallthrough(x) {
|
||||
switch (x) {
|
||||
case 1:
|
||||
console.log('one');
|
||||
case 2:
|
||||
console.log('two');
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// no-case-declarations
|
||||
function caseDeclarations(x) {
|
||||
switch (x) {
|
||||
case 1:
|
||||
let caseLocal = 10;
|
||||
console.log(caseLocal);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// no-duplicate-case
|
||||
function duplicateCase(x) {
|
||||
switch (x) {
|
||||
case 1:
|
||||
break;
|
||||
case 1:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// no-dupe-else-if
|
||||
function dupeElseIf(x) {
|
||||
if (x === 1) {
|
||||
return 'a';
|
||||
} else if (x === 1) {
|
||||
return 'b';
|
||||
}
|
||||
return 'c';
|
||||
}
|
||||
|
||||
// default-case
|
||||
function noDefault(x) {
|
||||
switch (x) {
|
||||
case 1:
|
||||
return 'one';
|
||||
}
|
||||
return 'none';
|
||||
}
|
||||
|
||||
// default-case-last
|
||||
function defaultNotLast(x) {
|
||||
switch (x) {
|
||||
default:
|
||||
return 'default';
|
||||
case 1:
|
||||
return 'one';
|
||||
}
|
||||
}
|
||||
|
||||
// no-constant-condition covered in syntax
|
||||
|
||||
// ===== 原 functions.js =====
|
||||
// ============ functions.js ============
|
||||
// 函数相关规则
|
||||
|
||||
// no-func-assign
|
||||
function assignableFn() {
|
||||
return 'original';
|
||||
}
|
||||
assignableFn = function () {
|
||||
return 'reassigned';
|
||||
};
|
||||
|
||||
// no-return-assign covered in async.js
|
||||
// no-throw-literal covered in async.js
|
||||
|
||||
// no-useless-vars? no such rule
|
||||
|
||||
// for-direction
|
||||
function forDirection() {
|
||||
for (let i = 0; i < 10; i--) {
|
||||
console.log(i);
|
||||
}
|
||||
}
|
||||
|
||||
// ===== 原 modern.js =====
|
||||
// ============ modern.js ============
|
||||
// 现代语法 / 风格偏好规则
|
||||
|
||||
// camelcase
|
||||
const my_snake_case_var = 1;
|
||||
|
||||
// new-cap
|
||||
const generated = new object();
|
||||
|
||||
// no-useless-concat
|
||||
const uselessConcat = 'foo' + 'bar';
|
||||
|
||||
// no-useless-computed-key
|
||||
const obj = { ['key']: 'value' };
|
||||
|
||||
// no-dupe-keys
|
||||
const dupeKeyObj = { a: 1, a: 2 };
|
||||
|
||||
// no-useless-rename
|
||||
const { a: a } = someObj;
|
||||
|
||||
// no-var covered in variables.js
|
||||
|
||||
// prefer-template
|
||||
function preferTemplate(name) {
|
||||
return 'Hello, ' + name + '!';
|
||||
}
|
||||
|
||||
// prefer-object-spread
|
||||
function preferSpreadStyle() {
|
||||
const merged = Object.assign({}, { a: 1 }, { b: 2 });
|
||||
return merged;
|
||||
}
|
||||
|
||||
// prefer-rest-params
|
||||
function preferRest() {
|
||||
const args = Array.prototype.slice.call(arguments);
|
||||
return args;
|
||||
}
|
||||
|
||||
// prefer-spread
|
||||
function spreadThis() {
|
||||
return 1;
|
||||
}
|
||||
function preferSpreadCall() {
|
||||
const nums = [1, 2, 3];
|
||||
return spreadThis.apply(null, nums);
|
||||
}
|
||||
|
||||
// prefer-object-has-own
|
||||
function hasOwnPrefer(obj, key) {
|
||||
return Object.prototype.hasOwnProperty.call(obj, key);
|
||||
}
|
||||
|
||||
// prefer-const
|
||||
function preferConstDemo() {
|
||||
let x = 1;
|
||||
return x;
|
||||
}
|
||||
|
||||
const someObj = { a: 1, b: 2, key: 'v' };
|
||||
|
||||
// ===== 原 scope.js =====
|
||||
// ============ scope.js ============
|
||||
// 作用域相关规则
|
||||
|
||||
// no-with
|
||||
// 说明:with 语句在严格模式(ESM/use strict)下是语法错误,无法在普通 script 中安全演示,
|
||||
// 故此处移除。该规则仍可通过 .eslintrc 的 parserOptions 演示,或使用 eval('with(...)') 场景。
|
||||
|
||||
// no-extend-native
|
||||
function extendNative() {
|
||||
String.prototype.padOut = function () {};
|
||||
}
|
||||
|
||||
// no-eval
|
||||
function evalDemo(input) {
|
||||
return eval(input);
|
||||
}
|
||||
|
||||
// no-param-reassign
|
||||
function paramReassign(x) {
|
||||
x = x * 2;
|
||||
return x;
|
||||
}
|
||||
|
||||
// no-unmodified-loop-condition
|
||||
function unmodifiedLoop(i) {
|
||||
while (i < 10) {
|
||||
console.log(i);
|
||||
}
|
||||
}
|
||||
|
||||
// no-unreachable-loop
|
||||
function unreachableLoop() {
|
||||
const items = [1, 2, 3];
|
||||
for (const item of items) {
|
||||
break;
|
||||
}
|
||||
while (true) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// no-unmodified-loop-condition already covered
|
||||
|
||||
// no-unreachable-loop already covered
|
||||
|
||||
// ===== 原 syntax.js =====
|
||||
// ============ syntax.js ============
|
||||
// 语法类规则:正则、转义、字面量语法问题
|
||||
|
||||
// no-invalid-regexp
|
||||
const badRegex = new RegExp('[');
|
||||
|
||||
// no-regex-spaces
|
||||
const regexSpaces = /a b/;
|
||||
|
||||
// no-control-regex
|
||||
const controlRegex = /\x1f/;
|
||||
|
||||
// no-empty-character-class
|
||||
const emptyCharClass = /abc[]/;
|
||||
|
||||
// no-misleading-character-class
|
||||
const misleadingCharClass = /[👍👎]/g;
|
||||
|
||||
// no-useless-backreference
|
||||
const uselessBackref = /(\1a)/;
|
||||
|
||||
// no-useless-escape
|
||||
const uselessEscape = 'line\.';
|
||||
|
||||
// no-irregular-whitespace
|
||||
const irregularWhitespace = 'dissimilar\u00A0space';
|
||||
|
||||
// no-loss-of-precision
|
||||
const lossOfPrecision = 2.1234567890123456789012345678901234567890;
|
||||
|
||||
// no-sparse-arrays
|
||||
const sparseArray = [1, , 2];
|
||||
|
||||
// no-empty
|
||||
function emptyFunctionBody() {}
|
||||
|
||||
// no-empty (related with no-empty-function? no, plain block)
|
||||
if (sparseArray.length > 0) {
|
||||
}
|
||||
|
||||
// no-constant-binary-expression
|
||||
const constantBinary = 'a' && 'b';
|
||||
|
||||
// no-constant-condition
|
||||
if (true) {
|
||||
console.log('always true');
|
||||
}
|
||||
|
||||
// no-cond-assign
|
||||
let condVar;
|
||||
if (condVar = 1) {
|
||||
console.log('assignment in condition');
|
||||
}
|
||||
|
||||
// no-self-assign
|
||||
let selfAssigned = 5;
|
||||
selfAssigned = selfAssigned;
|
||||
|
||||
// no-debugger
|
||||
debugger;
|
||||
|
||||
// no-extra-boolean-cast
|
||||
if (!!lossOfPrecision) {
|
||||
console.log('double negation');
|
||||
}
|
||||
|
||||
// no-ex-assign
|
||||
try {
|
||||
throw new Error('boom');
|
||||
} catch (e) {
|
||||
e = new Error('reassigned');
|
||||
}
|
||||
|
||||
// no-empty-pattern
|
||||
function noEmptyPattern({}) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// no-unreachable
|
||||
function unreachableCode() {
|
||||
return 1;
|
||||
console.log('never reached');
|
||||
}
|
||||
|
||||
// no-unexpected-multiline
|
||||
const unexpectedMultiline = lossOfPrecision
|
||||
(function () {
|
||||
console.log('IIFE');
|
||||
})();
|
||||
|
||||
// no-unsafe-optional-chaining
|
||||
function unsafeOptional(obj) {
|
||||
return (obj?.a).b;
|
||||
}
|
||||
|
||||
// no-unsafe-negation
|
||||
let flagFlag = true;
|
||||
const holderObj = {};
|
||||
if (!flagFlag in holderObj) {
|
||||
console.log('negated in');
|
||||
}
|
||||
|
||||
// no-unsafe-finally
|
||||
function unsafeFinally() {
|
||||
try {
|
||||
throw new Error('inner');
|
||||
} finally {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// no-unused-labels
|
||||
unusedLabel: for (let i = 0; i < 3; i++) {
|
||||
console.log(i);
|
||||
}
|
||||
|
||||
// no-unused-vars
|
||||
const completelyUnusedVariable = 42;
|
||||
|
||||
// no-useless-catch
|
||||
function uselessCatch() {
|
||||
try {
|
||||
return 1;
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
// no-useless-escape already covered above
|
||||
|
||||
// no-irregular-whitespace already covered
|
||||
|
||||
// no-empty-character-class already covered
|
||||
|
||||
// no-control-regex already covered
|
||||
|
||||
// no-regex-spaces already covered
|
||||
|
||||
// no-invalid-regexp already covered
|
||||
|
||||
// no-nonoctal-decimal-escape already covered
|
||||
// no-irregular-whitespace (actual NBSP character on this line)
|
||||
const irregularWS = 1;
|
||||
|
||||
// ===== 原 variables.js =====
|
||||
// ============ variables.js ============
|
||||
// 变量声明类规则
|
||||
|
||||
// no-var
|
||||
var oldStyleVar = 1;
|
||||
|
||||
// no-undef
|
||||
console.log(undefinedGlobalThing);
|
||||
|
||||
// no-redeclare
|
||||
function redeclareDemo() {
|
||||
var redeclaredName = 1;
|
||||
var redeclaredName = 2;
|
||||
}
|
||||
|
||||
// no-dupe-args
|
||||
// 说明:function(a, a) 重复形参在严格模式是语法错误(Argument name clash),
|
||||
// 已由 class DupeMember 的 no-dupe-class-members 覆盖"重复成员"演示,此处移除。
|
||||
|
||||
// no-delete-var
|
||||
// 说明:严格模式下 delete 局部变量是语法错误(Deleting local variable in strict mode),
|
||||
// 此处改为删除对象属性以保留 no-delete-var 规则的演示语义(delete 一元操作符)。
|
||||
function deleteVarDemo() {
|
||||
const obj = { localVar: 1 };
|
||||
delete obj.localVar;
|
||||
}
|
||||
|
||||
// no-shadow-restricted-names
|
||||
function shadowRestricted(undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// no-global-assign
|
||||
function globalAssign() {
|
||||
Math = {};
|
||||
}
|
||||
|
||||
// no-func-assign (in functions.js maybe)
|
||||
// no-class-assign (in classes.js)
|
||||
|
||||
// block-scoped-var
|
||||
function blockScopedDemo() {
|
||||
if (true) {
|
||||
var blockScoped = 1;
|
||||
}
|
||||
console.log(blockScoped);
|
||||
}
|
||||
|
||||
// no-unassigned-vars
|
||||
function unassignedVarDemo() {
|
||||
let unassignedVariable;
|
||||
return unassignedVariable;
|
||||
}
|
||||
|
||||
// no-shadow
|
||||
function shadowDemo() {
|
||||
let value = 1;
|
||||
function inner() {
|
||||
let value = 2;
|
||||
return value;
|
||||
}
|
||||
return inner();
|
||||
}
|
||||
|
||||
// no-useless-assignment
|
||||
function uselessAssignment() {
|
||||
let x = 1;
|
||||
x = 2;
|
||||
return x;
|
||||
}
|
||||
|
||||
// no-array-constructor
|
||||
const arrayViaConstructor = new Array(1, 2, 3);
|
||||
|
||||
// no-new-native-nonconstructor
|
||||
const symbolViaNew = new Symbol('x');
|
||||
|
||||
// no-obj-calls
|
||||
const jsonAsFunction = JSON();
|
||||
|
||||
// no-const-assign
|
||||
const immutableValue = 1;
|
||||
immutableValue = 2;
|
||||
|
||||
// no-prototype-builtins
|
||||
function protoBuilds(obj) {
|
||||
return obj.hasOwnProperty('key');
|
||||
}
|
||||
|
||||
// no-import-assign
|
||||
// 说明:本文件为普通 script(无 import/export),no-import-assign 需要 ESM 模块绑定,
|
||||
// 该规则由 typesmodule.ts(ESM 模块文件)演示,此处移除占位。
|
||||
@@ -0,0 +1,11 @@
|
||||
// ============ esm-demo.js ============
|
||||
// ESM 模块文件:演示 no-import-assign(module 模式下对 import 绑定赋值)
|
||||
|
||||
// no-import-assign
|
||||
import fs from 'fs';
|
||||
|
||||
function reassignImport() {
|
||||
fs = 1;
|
||||
}
|
||||
|
||||
console.log(fs);
|
||||
@@ -0,0 +1,36 @@
|
||||
/* eslint preserve-caught-error: "error" */
|
||||
// ============ legacysyntax.cjs ============
|
||||
// .cjs 扩展名使 ESLint flat config 默认按 commonjs(宽松)模式解析本文件,
|
||||
// 无需任何工程配置(eslint.config.mjs)即可触发仅 sloppy 模式合法的旧式语法规则。
|
||||
// 首行行内注释单独启用 preserve-caught-error(eslint 9.39.4 引擎已实现该规则,但内置配置未启用)。
|
||||
|
||||
// no-nonoctal-decimal-escape
|
||||
const nonOctalEscape = '\8 and \9';
|
||||
|
||||
// 顺手在此以宽松模式再触发 no-octal(旧式八进制字面量)
|
||||
const legacyOctal = 010;
|
||||
|
||||
// ===== 补充:其余仅宽松模式合法的旧式语法样例 =====
|
||||
|
||||
// no-with
|
||||
with ({ pi: 3.14, e: 2.72 }) {
|
||||
console.log(pi, e);
|
||||
}
|
||||
|
||||
// no-delete-var
|
||||
var deletable = 42;
|
||||
delete deletable;
|
||||
|
||||
// no-dupe-args
|
||||
function duplicateArgs(a, a) {
|
||||
return a;
|
||||
}
|
||||
|
||||
// preserve-caught-error(catch 中抛新错误应保留原始错误)
|
||||
try {
|
||||
JSON.parse('not json');
|
||||
} catch (err) {
|
||||
throw new Error('parse failed');
|
||||
}
|
||||
|
||||
console.log(nonOctalEscape, legacyOctal, duplicateArgs(1, 2));
|
||||
@@ -0,0 +1,34 @@
|
||||
// ============ legacysyntax.js ============
|
||||
// 该文件以 script(非严格)模式解析,用于触发需要在 sloppy 模式下才能合法的旧式转义规则。
|
||||
// eslint.config.mjs 中为其单独设置了 sourceType: "script"。
|
||||
|
||||
// no-nonoctal-decimal-escape
|
||||
const nonOctalEscape = '\8 and \9';
|
||||
|
||||
// 顺手在此以 script 模式再触发 no-octal(旧式八进制字面量)
|
||||
const legacyOctal = 010;
|
||||
|
||||
// ===== 补充:其余仅 sloppy 模式合法的旧式语法样例(原 common.js 中被移除的致命样例)=====
|
||||
|
||||
// no-with
|
||||
with ({ pi: 3.14, e: 2.72 }) {
|
||||
console.log(pi, e);
|
||||
}
|
||||
|
||||
// no-delete-var
|
||||
var deletable = 42;
|
||||
delete deletable;
|
||||
|
||||
// no-dupe-args
|
||||
function duplicateArgs(a, a) {
|
||||
return a;
|
||||
}
|
||||
|
||||
// preserve-caught-error(ESLint 10 新增,catch 中抛新错误应保留原始错误)
|
||||
try {
|
||||
JSON.parse('not json');
|
||||
} catch (err) {
|
||||
throw new Error('parse failed');
|
||||
}
|
||||
|
||||
console.log(nonOctalEscape, legacyOctal, duplicateArgs(1, 2));
|
||||
@@ -0,0 +1,213 @@
|
||||
// ============ typescript.ts ============
|
||||
// TypeScript 规则演示(覆盖 ts.configs.recommended + 插件精选规则)
|
||||
|
||||
// triple-slash-reference: 禁止非类型导入的三斜线指令
|
||||
/// <reference path="./typesmodule.ts" />
|
||||
|
||||
// consistent-type-imports
|
||||
import { DemoType } from './typesmodule';
|
||||
|
||||
// no-require-imports / no-var
|
||||
function legacyLoad() {
|
||||
var mod = require('./typesmodule');
|
||||
return mod;
|
||||
}
|
||||
|
||||
// no-array-constructor
|
||||
function arrayConstructor() {
|
||||
return new Array(1, 2, 3);
|
||||
}
|
||||
|
||||
// no-dynamic-delete
|
||||
function dynamicDelete(obj: Record<string, unknown>, key: string) {
|
||||
delete obj[key];
|
||||
}
|
||||
|
||||
// no-extraneous-class
|
||||
class UtilityClass {
|
||||
static helper(): number {
|
||||
return 42;
|
||||
}
|
||||
}
|
||||
|
||||
// no-invalid-void-type
|
||||
function invalidVoid(): void | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// no-non-null-asserted-nullish-coalescing
|
||||
function nullishAsserted(input: string | null) {
|
||||
const value: string | null = input;
|
||||
return value! ?? 'fallback';
|
||||
}
|
||||
|
||||
// no-non-null-assertion 与 no-extra-non-null-assertion
|
||||
function nonNullAssert(value: string | null) {
|
||||
return value!!.length;
|
||||
}
|
||||
|
||||
// no-non-null-asserted-optional-chain
|
||||
function optionalChainAsserted(obj: { a?: { b?: string } }) {
|
||||
return obj?.a?.b!;
|
||||
}
|
||||
|
||||
// no-this-alias
|
||||
function thisAlias(this: { x: number }) {
|
||||
const self = this;
|
||||
return self.x;
|
||||
}
|
||||
|
||||
// no-shadow 与 prefer-const
|
||||
function shadowInTs() {
|
||||
const shadowed = 1;
|
||||
const inner = () => {
|
||||
const shadowed = 2;
|
||||
return shadowed;
|
||||
};
|
||||
return inner();
|
||||
}
|
||||
|
||||
// prefer-const(@typescript-eslint/prefer-const:重新赋值前不应使用 let)
|
||||
function preferConstInTs() {
|
||||
let fixed = 1;
|
||||
return fixed;
|
||||
}
|
||||
|
||||
// no-useless-constructor
|
||||
class EmptyConstructor {
|
||||
constructor() {}
|
||||
}
|
||||
|
||||
// no-useless-empty-export
|
||||
export {};
|
||||
|
||||
// prefer-enum-initializers 与 no-duplicate-enum-values
|
||||
enum UninitializedEnum {
|
||||
First,
|
||||
Second,
|
||||
}
|
||||
enum DuplicateEnum {
|
||||
A = 1,
|
||||
B = 1,
|
||||
}
|
||||
|
||||
// prefer-literal-enum-member
|
||||
const someValue = 10;
|
||||
enum NonLiteralEnum {
|
||||
A = someValue,
|
||||
}
|
||||
|
||||
// unified-signatures
|
||||
function unifiedDemo(a: string): void;
|
||||
function unifiedDemo(a: 'x'): void;
|
||||
function unifiedDemo(a: string): void {
|
||||
console.log(a);
|
||||
}
|
||||
|
||||
// no-explicit-any
|
||||
function explicitAny(value: any): any {
|
||||
return value;
|
||||
}
|
||||
|
||||
// no-empty-object-type
|
||||
type EmptyObject = {};
|
||||
interface EmptyInterface {}
|
||||
const emptyObj: EmptyObject = {};
|
||||
|
||||
// no-wrapper-object-types
|
||||
function wrapperTypes(s: String, n: Number, b: Boolean) {
|
||||
return s + n + b;
|
||||
}
|
||||
|
||||
// no-unsafe-function-type
|
||||
type UnsafeFn = Function;
|
||||
function unsafeFn(fn: UnsafeFn) {
|
||||
fn();
|
||||
}
|
||||
|
||||
// no-unused-expressions
|
||||
function unusedExpr(flag: boolean, value: number) {
|
||||
flag && value;
|
||||
value;
|
||||
}
|
||||
|
||||
// no-unnecessary-type-constraint
|
||||
function pointlessConstraint<T extends unknown>(t: T): T {
|
||||
return t;
|
||||
}
|
||||
|
||||
// no-unsafe-declaration-merging
|
||||
interface MergedThing {
|
||||
method(): void;
|
||||
}
|
||||
class MergedThing {
|
||||
value = 1;
|
||||
}
|
||||
|
||||
// ban-ts-comment
|
||||
function tsIgnored() {
|
||||
// @ts-ignore
|
||||
const x = 1;
|
||||
return x;
|
||||
}
|
||||
|
||||
// no-misused-new
|
||||
interface MisusedNew {
|
||||
new (): MisusedNew;
|
||||
constructor(): void;
|
||||
}
|
||||
|
||||
// no-namespace 与 prefer-namespace-keyword
|
||||
namespace LegacyNamespace {
|
||||
export const value = 1;
|
||||
}
|
||||
module LegacyModule {
|
||||
export const value = 2;
|
||||
}
|
||||
|
||||
// prefer-as-const
|
||||
function preferAsConst() {
|
||||
const literal: 'hello' = 'hello';
|
||||
return literal;
|
||||
}
|
||||
|
||||
// prefer-rest-params 与 prefer-spread
|
||||
function restParams() {
|
||||
const args = arguments;
|
||||
return Array.prototype.slice.call(args);
|
||||
}
|
||||
declare function mySpreadFn(...args: number[]): number;
|
||||
function spreadArgs(base: number[]) {
|
||||
return mySpreadFn.apply(undefined, base);
|
||||
}
|
||||
|
||||
// preserve-caught-error(ESLint 10 新增,catch 中抛新错误应保留原始错误)
|
||||
function swallowError(input: string): number {
|
||||
try {
|
||||
return JSON.parse(input) as number;
|
||||
} catch (err) {
|
||||
throw new Error('parse failed');
|
||||
}
|
||||
}
|
||||
|
||||
const demoValue: DemoType = 'demo';
|
||||
void demoValue;
|
||||
void swallowError;
|
||||
|
||||
export {
|
||||
dynamicDelete,
|
||||
nullishAsserted,
|
||||
nonNullAssert,
|
||||
shadowInTs,
|
||||
unifiedDemo,
|
||||
explicitAny,
|
||||
wrapperTypes,
|
||||
unsafeFn,
|
||||
unusedExpr,
|
||||
pointlessConstraint,
|
||||
preferAsConst,
|
||||
restParams,
|
||||
spreadArgs,
|
||||
LegacyNamespace,
|
||||
LegacyModule,
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
// ============ typesmodule.ts ============
|
||||
// 供 typescript.ts 使用的类型模块
|
||||
|
||||
export interface stringifyType {
|
||||
(input: string): string;
|
||||
}
|
||||
|
||||
export type DemoType = string | number;
|
||||
Reference in New Issue
Block a user