Rome 的 noBannedTypes 规则:禁用原始类型别名、Function 与误导性的 `{}` 类型
2026/9/20 9:21:05 网站建设 项目流程
  • 开发工具
  • CLI
  • Lint
  • 格式化
  • 静态分析
  • 代码质量
  • 构建工具

【免费下载链接】tools

Unified developer tools for JavaScript, TypeScript, and the web

项目地址:https://gitcode.com/gh_mirrors/to/tools
点击查看免费下载

noBannedTypes是 Rome(当前仓库gh_mirrors/to/tools,即 "Unified developer tools for JavaScript, TypeScript, and the web")在 TypeScript 语义分析中提供的一条 lint 规则,自 v10.0.0 起进入 nursery(试验)阶段。它用于禁止三类容易引入类型事故的写法:原始类型(primitive type)的大写别名(如StringBoolean)、过度宽泛的Function类型,以及含义与直觉相悖的非空类型{}。读完本文,你将掌握该规则的三条核心约束、两条内置例外、安全修复(Safe fix)的实际行为,以及如何通过rome.json和 CLI 参数启用、关闭或调整这条规则。

规则概述:一条规则,三类约束

规则文档(website/src/pages/lint/rules/noBannedTypes.md)将其能力概括为一句话:Disallow primitive type aliases and misleading types(禁止原始类型别名和具有误导性的类型)。具体拆解为以下三部分:

  1. 强制原始类型命名一致:原始类型存在别名,例如Numbernumber的别名。规则推荐统一使用小写原始类型名。
  2. 禁止Function类型Function是宽松类型(loosely typed),被视为危险或有害的用法。它等价于使用了不安全any(...rest: any[]) => any
  3. 禁止具有误导性的非空类型{}:在 TypeScript 中{}并不表示"空对象",而是表示"除nullundefined之外的任何值",例如下面的代码是完全合法的:
const n: {} = 0

该规则从 typescript-eslint 的 ban-types)。

规则在仓库中的定位

  • 所属分组lint/nursery(试验性规则组),注册于 crates/rome_js_analyze/src/semantic_analyzers/nursery.rs,并在 crates/rome_diagnostics_categories/src/categories.rs 中登记了诊断分类。
  • 推荐启用:规则声明中recommended: true(见 no_banned_types.rs),即按推荐配置运行时默认开启。
  • 实现方式:规则的查询类型为Semantic<TsBannedType>,其中TsBannedTypeTsReferenceType | TsObjectType的联合节点(见 no_banned_types.rs),说明它运行在语义分析阶段,会结合绑定信息(binding model)判断标识符是否为全局类型。

约束一:禁止原始类型大写别名

TypeScript 中的原始类型拥有对应的大写包装器对象类型:NumberBooleanStringSymbolBigIntObject。这些大写形式作为类型注解使用时几乎总是错误的选择——它们描述的是包装器对象而非原始值,且与团队其他成员书写的小写形式不一致。

从源码看,规则通过BannedType::from_str维护了一份完整的"黑名单"(见 no_banned_types.rs):

fn from_str(s: &str) -> Option<Self> { Some(match s { "BigInt" => Self::BigInt, "Boolean" => Self::Boolean, "Function" => Self::Function, "Number" => Self::Number, "Object" => Self::Object, "String" => Self::String, "Symbol" => Self::Symbol, "{}" => Self::EmptyObject, _ => return None, }) }

对于这些原始类型别名,诊断消息统一为 "Use lowercase primitives for consistency."(使用小写原始类型以保持一致),并附带一条Safe fix(安全修复),把大写标识符原地替换为小写关键字。修复由fix_with()实现:它把BigInt/Boolean/Number/String/Symbol映射到对应的语法关键字BIGINT_KW/BOOLEAN_KW/NUMBER_KW/STRING_KW/SYMBOL_KW,再通过rome_js_factory生成新的引用标识符节点(见 no_banned_types.rs)。注意FunctionObject{}因为无法机械地改写为某个关键字,不会提供安全修复

无效示例(Invalid)

let foo: String = "bar";

运行规则后,Rome 会报告如下诊断(完整快照见 crates/rome_js_analyze/tests/specs/nursery/noBannedTypes/invalid.ts.snap):

nursery/noBannedTypes.js:1:10 lint/nursery/noBannedTypes FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━ ✖ Don't use 'String' as a type. > 1 │ let foo: String = "bar"; │ ^^^^^^ ℹ Use lowercase primitives for consistency. ℹ Safe fix: Use 'string' instead 1 │ - let foo: String = "bar"; 1 │ + let foo: string = "bar";

其他同类无效示例:

let bool = true as Boolean; // 建议改为 boolean let invalidTuple: [string, Boolean] = ["foo", false]; // 建议改为 boolean

规则对as断言、元组、泛型参数、类继承、接口实现等所有类型位置一视同仁。测试用例(invalid.ts)覆盖了函数参数、Array<String>、联合类型String | Object、泛型默认值class Foo<T = String>等大量场景。

有效示例(Valid)

let foo: string = "bar"; let tuple: [boolean, string] = [false, "foo"];

关键例外:允许用户自定义同名类型

规则不是简单地"见String就报"。在run中,规则先通过model.binding(reference_identifier).is_none()检查该标识符是否绑定了本地声明:只有当它未被绑定(即全局标识符)时才触发诊断(见 no_banned_types.rs)。因此,用户完全可以在局部作用域内合法地声明并使用自己的Number别名,例如 valid.ts 中的用例:

namespace X { // Allow user aliases type Number = number function f(): Number { return 0; } }

这段代码不会触发noBannedTypes,因为这里的Number绑定到了用户自定义的类型别名,而非全局包装器类型。

约束二:禁止Function类型

Function类型在 TypeScript 中几乎不提供任何类型信息:它接受任意函数形态的值,等价于(...rest: any[]) => any,底层依赖不安全的any。用它标注变量,等于放弃了参数与返回值的所有静态检查,是常见 bug 的来源。

触发诊断时,规则给出的提示为:

✖ Don't use 'Function' as a type. ℹ Prefer explicitly define the function shape. This type accepts any function-like value, which can be a common source of bugs.

例如以下代码会被报告(见 invalid.ts):

let fn: Function = () => true;

正确做法是显式声明函数形态,例如() => boolean(x: number) => string,或使用函数接口。由于Function没有对应的安全关键字替换,规则对它是只报告、不自动修复

约束三:禁止误导性的非空类型{}

这是最容易让人踩坑的一条。在 TypeScript 中:

  • {}表示"除nullundefined之外的任何值",不是空对象
  • 因此const n: {} = 0完全合法,但这几乎肯定不是写代码的人的本意。

规则对此的报告如下(对应 website/src/pages/lint/rules/noBannedTypes.md 中的示例):

✖ Don't use '{}' as a type. ℹ Prefer explicitly define the object shape. '{}' means "any non-nullable value".

如果需要真正表示"空对象",文档推荐使用以下两种等价写法之一:

{ [k: string]: never } Record<string, never>

两条内置例外

为避免误伤合理的泛型约束写法,规则对{}给出了两个例外(文档说明见 noBannedTypes.md,实现见 no_banned_types.rs):

  1. 泛型约束:用T extends {}将泛型参数限制为非空类型时允许。源码中通过检查ts_object_type.parent::<TsTypeConstraintClause>().is_none()来放行约束子句中的{}
function f<T extends {}>(x: T) { assert(x != null); }
  1. 类型交叉(intersection):用T & {}把某个类型收窄为其非空等价形式时允许。源码通过ts_object_type.parent::<TsIntersectionTypeElementList>().is_none()放行交叉类型成员列表中的{}
type NonNullableMyType = MyType & {};

这两个例外在 valid.ts 中都有对应测试:

type PhoneNumber = number | null | undefined; type NonNullablePhoneNumber = PhoneNumber & {}; function consumeNonNullableValue<T extends {}>(value: T) {}

对于最后一种交叉类型的场景,如果只是想表达"非空",文档还建议直接用 TypeScript 自带的工具类型,语义更清晰:

type NonNullableMyType = NonNullable<MyType>;

其余有效写法

以下写法都不会触发规则(测试见 valid.ts):

let f = Object(); // 作为构造函数调用而非类型注解 let g = Object.create(null); let h = String(false); // 作为函数调用 let b: undefined; let c: null; let a: []; type Props = { foo: string; } // 显式定义对象形状

注意Object作为函数或构造函数调用(如Object()String(false))时不受影响,规则只拦截它作为类型注解出现的位置。

配置与使用方式

通过 rome.json 配置

noBannedTypes属于linter.rules.nursery分组,可在rome.json中按需开启/关闭或降级为警告:

{ "linter": { "rules": { "nursery": { "noBannedTypes": "off" } } } }

取值为"on" | "off" | "warn"。由于该规则recommended: true,在推荐配置下默认启用;若希望关闭,可将取值设为"off"

通过 CLI 参数配置

从配置生成代码(crates/rome_service/src/configuration/linter/rules.rs)可以看到,每条规则都对应一个命令行参数,noBannedTypes对应:

rome check --linter-rules-nursery-no-banned-types=on src/ rome check --linter-rules-nursery-no-banned-types=off src/ rome check --linter-rules-nursery-no-banned-types=warn src/

参数同样接受on|off|warn三种取值。noBannedTypes的启用状态最终会通过is_enabled()汇入规则过滤集合,由分析框架统一调度(见 rules.rs)。

针对单条规则的禁用与配置

若只需临时禁用某一行或某个文件的该规则,可参考项目文档中关于禁用 lint 规则与规则选项的通用说明(linter 页面中的 Disable a lint rule 与 Rule options 章节):

  • 行内禁用:使用// rome-ignore lint/nursery/noBannedTypes: <reason>注释,Rome 会据此跳过该行诊断;
  • 规则选项:通过配置对象为规则提供细化参数。

源码阅读指引

想深入理解这条规则,建议按以下路径阅读仓库:

  1. 规则实现:crates/rome_js_analyze/src/semantic_analyzers/nursery/no_banned_types.rs —— 完整包含declare_rule!宏声明、run(查询与匹配)、diagnostic(诊断消息)和action(安全修复)四个核心部分;
  2. 注册与分组:crates/rome_js_analyze/src/semantic_analyzers/nursery.rs —— 规则模块的注册入口;
  3. 配置生成:crates/rome_service/src/configuration/linter/rules.rs ——no-banned-types命令行参数与nursery.noBannedTypes配置字段的定义;
  4. 测试用例:invalid.ts 与 valid.ts,及其快照 invalid.ts.snap / valid.ts.snap —— 覆盖了全部禁止场景与例外场景,是理解规则边界的直接素材。

小结

noBannedTypes通过三把"闸门"提升 TypeScript 代码的类型严谨性:强制大写原始类型别名回归小写(并提供安全修复)、拒绝宽泛的Function、纠正对{}的误解(同时为泛型约束与交叉类型保留例外)。结合 no_banned_types.rs 的实现与 invalid.ts / valid.ts 的测试,你可以准确预判它在自己代码库中的行为边界,并借助rome.json或 CLI 参数按团队规范灵活调整。

  • 开发工具
  • CLI
  • Lint
  • 格式化
  • 静态分析
  • 代码质量
  • 构建工具

【免费下载链接】tools

Unified developer tools for JavaScript, TypeScript, and the web

项目地址:https://gitcode.com/gh_mirrors/to/tools
点击查看免费下载

相关推荐

上一篇:3步打造专属Electron开发脚手架:Electron Fiddle模板深度定制指南
下一篇:Whiteboard架构解析:实时协作白板的实现原理与技术栈

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询