十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

eslint-plugin-unicorn 的 no-unsafe-property-key 规则:全面禁止不安全属性键

eslint-plugin-unicorn 的 no-unsafe-property-key 规则:全面禁止不安全属性键 eslint-plugin-unicorn 的 no-unsafe-property-key 规则全面禁止不安全属性键【免费下载链接】eslint-plugin-unicornMore than 300 powerful ESLint rules项目地址: https://gitcode.com/GitHub_Trending/es/eslint-plugin-unicorn导读no-unsafe-property-key是 eslint-plugin-unicorn 提供的一条问题定位型type: problem规则用于禁止将不安全的值用作 JavaScript 属性键。本文以官方文档 docs/rules/no-unsafe-property-key.md 为主体结合规则源码 rules/no-unsafe-property-key.js 与测试用例 test/no-unsafe-property-key.js完整讲解该规则的原理、检测范围、三级类型推断机制静态值 → TS 类型标注 → TS 类型信息、合法豁免场景与工程配置方式帮助你彻底规避对象键被隐式字符串化这类隐蔽 bug。为什么要禁止不安全的属性键JavaScript 规定属性键只能是字符串string或符号symbol。当开发者把其他类型的值用作属性键时引擎不会报错而是先对其执行隐式字符串化coercion再使用。这种宽容的行为恰恰会掩盖大量真实 bug**对象object**被字符串化后变成[object Object]不同对象碰撞到同一个键**数组array**被字符串化后变成以逗号连接的字符串如[]→、[1,2]→1,2BigInt被字符串化后丢失n后缀4n→4可能与普通数字4的键发生意外碰撞不安全的数字超出安全整数范围的数、非有限数被字符串化后产生令人困惑的键名例如9007199254740992会被规范化为9007199254740992而Infinity、NaN则变成Infinity、NaN。因此本规则给出的总原则是当确实想进行字符串化时请显式写出String(key)或key.join()之类的字符串键当对象身份identity才是键的语义时请改用Map或WeakMap它们支持以任意对象作为键。规则在项目中的状态根据 readme.md 的规则总表no-unsafe-property-key的定位为Disallow unsafe values as property keys.它在recommended✅配置中默认启用在unopinionated☑️配置中默认禁用并且不提供任何可配置的选项schema: []属于开箱即用、无需调参的规则。官方示例错误写法与正确写法以下示例全部来自规则文档展示了最常见的不安全属性键场景及对应修复方案。对象字面量作为键// ❌ 对象被隐式字符串化为 [object Object] const key {}; object[key] value; // ✅ 显式字符串化 object[String(key)] value;数组作为键// ❌ 数组被隐式字符串化为以逗号连接的字符串 const key []; const object { [key]: value, }; // ✅ 显式拼接字符串 const object { [key.join()]: value, };BigInt 作为键// ❌ 4n 被字符串化后丢失 n 后缀变成 4 array[4n] value; // ✅ 显式转换为 Number array[Number(4n)] value;超出安全整数范围的数字作为键// ❌ 9007199254740992 超出 Number.MAX_SAFE_INTEGER字符串化结果可能与直觉不符 const object { 9007199254740992: value, }; // ✅ 显式写成字符串键意图清晰 const object { 9007199254740992: value, };需要对象身份时改用 Map// ❌ 对象作为键被隐式字符串化所有对象都变成同一个 [object Object] 键 const object { [{}]: value, }; // ✅ 用 Map 以对象身份作为键 const map new Map([ [{}, value], ]);规则的检测范围到底检查哪些写法从 rules/no-unsafe-property-key.js 的create入口可以看出规则在两类节点上触发检查计算成员表达式computed MemberExpression如object[key]、object?.[key]、delete object[key]、object[key]()计算属性定义节点包括普通对象属性Property、类方法MethodDefinition、类字段PropertyDefinition、访问器属性AccessorProperty以及 TypeScript 抽象成员TSAbstractMethodDefinition、TSAbstractPropertyDefinition、TSAbstractAccessorProperty即{[key]: value}、class A {[key]() {}}、class A {[key] value}等写法。同时规则做了精确的入口过滤shouldCheckPropertyDefinitionKey见 rules/no-unsafe-property-key.js非计算属性一律不检查object.key这种点访问天然是合法字符串键计算属性中只有键本身可能不安全时才深入分析非计算但键为 BigInt 或非安全数字字面量的写法也会检查例如{4n: value}、{9007199254740992: value}、class A {4n() {}}—— 这些写法虽然语法上是非计算但键值本身仍是不安全类型测试用例 test/no-unsafe-property-key.js 专门覆盖了这类场景。此外文档明确说明规则有意放过常见的安全数字索引例如array[0]、object[1]不会被报告——安全整数范围内的数字键是日常编码中广泛使用的合法模式。底层原理什么样的值算不安全键规则的判定核心集中在 rules/no-unsafe-property-key.js 的几个判别函数中可以归纳为以下几条明确规则AST 节点级判定isUnsafePropertyKeyNode只要属性键节点是以下类型之一直接判为不安全ObjectExpression对象字面量ArrayExpression数组字面量FunctionExpression/ArrowFunctionExpression函数表达式ClassExpression类表达式NewExpressionnew URL(...)、new Date()等构造调用BigInt 字面量含一元负号形式如-4n非安全数字字面量见下数字安全判定isUnsafeNumbertypeof value number !Number.isSafeInteger(value) (!Number.isFinite(value) || Object.is(value, Math.trunc(value)));即非安全整数超出Number.MAX_SAFE_INTEGER的整数且同时满足非有限数Infinity/NaN或本身就是整数的数字被视为不安全。换句话说规则放行了0.5、-0.5、1.25这类有限的小数——它们的字符串化结果精确且可预期测试用例中object[0.5]、object[-0.5]均为合法样例见 test/no-unsafe-property-key.js。静态值判定isUnsafeStaticValue/isSafeStaticValue通过静态求值rules/utils/get-static-value.js 提供的getStaticValueIfNoSideEffects得到常量值后不安全bigint、非安全数字、以及任意非null的object类型值安全null、string、symbol、boolean、undefined以及非不安全数字。全局标识符黑名单unsafeGlobalIdentifiers规则维护了一份全局名清单rules/no-unsafe-property-key.js当这些全局名被用作键时直接判为不安全包括内置错误类TypeError、Error系列来自 rules/shared/builtin-errors.js需要new调用的内置构造器与不需要new的内置函数来自 rules/utils/builtins.js 的disallowNew/enforceNew两个集合Array、Date、Math、JSON、Reflect、Symbol、Intl、Atomics、WebAssembly、globalThis、NaN、Infinity、parseInt、parseFloat、isNaN、isFinite、eval、decodeURI、encodeURI等。需要强调的是该黑名单只在确认变量是全局标识符时生效借助isGlobalIdentifier判断如果局部代码里定义了同名变量如const Array key、function foo(Date) {...}则不会被误报——测试用例 test/no-unsafe-property-key.js 专门验证了这一点。同理object[globalThis.Array]、object[globalThis[Math]]这类通过globalThis访问的全局属性也会被识别为不安全。其他被识别的不安全来源函数/类/枚举的声明引用function key() {} object[key]、class Key {} object[Key]、enum Key {A} object[Key]枚举整体被当作对象但枚举成员访问object[Key.A]是安全的因为枚举成员本质是字符串键别名链追踪const key {}; const alias key; object[alias]会被追踪到原始初始化值并判为不安全条件表达式只要consequent或alternate任一分支不安全整体即判为不安全object[condition ? {} : key]被报告序列表达式取最后一个表达式的判定结果object[(0, {})]被报告。三级类型推断如何减少误报这是本规则最精巧的部分它不满足于看到对象字面量就报警而是构建了一条静态值 → TypeScript 类型标注 → TypeScript 类型信息的递进分析链对应源码中getStaticPropertyKeyType→getIdentifierStaticPropertyKeyType→getTypeInformationPropertyKeyType的调用顺序见 rules/no-unsafe-property-key.js。第一级静态值与 AST 结构优先尝试静态求值、全局标识符识别和上述节点级判定。这一层能覆盖大多数纯 JavaScript 场景且完全不需要 TypeScript 类型信息。第二级TypeScript 类型标注无类型信息时当项目没有开启类型信息parserOptions中未配置project/programs/projectService时规则退而分析类型标注的语法结构isUnsafePropertyKeyTypeAnnotationWithScope见 rules/no-unsafe-property-key.js包括bigint、object、ArrayT、ReadonlyArrayT、数组/元组类型、函数/构造器类型 → 不安全类型字面量{id: string}、有成员的类型字面量 → 不安全接口interface有成员或继承自不安全类型 → 不安全联合类型任一成员不安全 → 不安全交叉类型所有成员不安全 → 不安全模板字符串类型feature:${string}→ 安全类型别名与接口的递归展开带visitedTypeNames防环readonly修饰符不影响判定。一个很有代表性的细节type Key string; declare const key: Key; object[key]是安全的而type Key {id: string}; ...; object[key]是不安全的当存在同名类型遮蔽时{ type Key string; ... }规则会基于作用域解析出的正确定义判定见 test/no-unsafe-property-key.js。第三级TypeScript 类型信息type-aware当配置了类型信息hasTypeInformationParserOptions检测programs、project、projectService任一非空见 rules/no-unsafe-property-key.js时规则通过parserServices获取节点的真实类型并做深度分析isPossiblyUnsafePropertyKeyType见 rules/no-unsafe-property-key.js类型参数递归分析其约束constraint联合/交叉类型分别采用任一不安全即不安全 / 全部不安全才不安全的策略模板字符串类型TypeFlags.TemplateLiteral见 rules/utils/types.js→ 安全字符串映射类型Uppercasestring、Lowercasestring、Capitalizestring、Uncapitalizestring见 rules/utils/types.js→ 安全因为它们是字符串子类型unique symbol类型含Symbol.iterator等 well-known symbol见 rules/utils/types.js→ 安全这是符号可作为属性键语义的正确体现带有可调用签名或可构造签名的类型 → 不安全默认库中的符号isDefaultLibrarySymbol见 rules/utils/types.js→ 不安全。在测试用例test/no-unsafe-property-key.js中可以看到类型信息的强大之处它能够对const key: Number 1包装类型对象安全与const key: number 9007199254740992字面量数值类型不安全作出区分能够识别typeof input string收窄后的联合类型分支是安全的、而typeof input object分支不安全甚至能穿透ReturnType() ...、typeof unsafe等类型运算。各层级如何协同最终判定逻辑getPropertyKeyProblem见 rules/no-unsafe-property-key.js是任一判定结果明确为不安全即报告只有静态值明确为安全才直接放过其余情况只要静态层、类型标注层、类型信息层都没有给出确定结论全部为unknown则不报告——这种存疑不报的保守策略有效控制了误报率。常见豁免场景一览推荐仔细阅读综合文档与测试用例以下写法不会被报告可作为合法键的参考基准字符串、模板字符串含带插值的feature:${from}键Symbol及其成员、unique symbol类型、枚举成员访问Key.A布尔、null、undefined作为键虽然奇怪但字符串化结果确定安全整数与有限小数数字键object[1]、object[0.5]、array[4]String(key)、key.join()、Number(4n)、bigint.toString()等显式转换结果const key key; object[key]const 常量追踪局部同名变量遮蔽全局黑名单const Math key类型收窄后确定为字符串/符号的变量typeof input string分支。在项目中的启用方式规则已在recommended配置中默认启用直接使用即可// .eslintrc.json { extends: [plugin:unicorn/recommended], rules: { // 规则无选项无需额外配置 // 若需在 recommended 基础上强制关闭 unicorn/no-unsafe-property-key: off } }使用扁平化配置flat config的项目可参考 configs/flat-config-base.js 的配置方式引入recommended预设。由于规则基于 AST 与类型分析开箱即用只有在需要类型信息增强分析时才需要在 parserOptions 中提供project或projectService配置可参考测试中的用法 test/no-unsafe-property-key.js。小结no-unsafe-property-key通过AST 静态分析 TS 类型标注 TS 类型信息三层递进判定精准识别对象、数组、函数、类、BigInt 与非安全数字等会被隐式字符串化的属性键并引导开发者改用显式字符串键或Map/WeakMap。它默认开启、零配置、误报控制严格是规避[object Object]类键碰撞 bug 的实用工具。想深入了解判定细节建议通读 rules/no-unsafe-property-key.js 与 test/no-unsafe-property-key.js 中 360 余行覆盖全面的正反例测试。【免费下载链接】eslint-plugin-unicornMore than 300 powerful ESLint rules项目地址: https://gitcode.com/GitHub_Trending/es/eslint-plugin-unicorn创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表