news 2026/9/14 9:51:54

typescript-eslint `ban-types` 规则移除与迁移指南:no-restricted-types 等四个替代规则的完整解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
typescript-eslint `ban-types` 规则移除与迁移指南:no-restricted-types 等四个替代规则的完整解析

typescript-eslintban-types规则移除与迁移指南:no-restricted-types 等四个替代规则的完整解析

【免费下载链接】typescript-eslint:sparkles: Monorepo for all the tooling which enables ESLint to support TypeScript项目地址: https://gitcode.com/GitHub_Trending/ty/typescript-eslint

本文是一份面向 ESLint + TypeScript 用户的迁移指南,核心围绕 typescript-eslint 中经典的ban-types规则在 v8 中被拆分、弃用并最终移除的完整过程展开。你将了解到ban-types为何被重构、它被拆分为哪四个新规则(no-restricted-typesno-empty-object-typeno-unsafe-function-typeno-wrapper-object-types),每个新规则的具体配置方法、源码实现原理、默认启用情况,以及如何把旧的ban-types配置平滑迁移到新规则,让升级到 typescript-eslint v8 不再困惑。

一、背景:ban-types规则为何被拆掉

在 typescript-eslint 的早期版本中,@typescript-eslint/ban-types是最具代表性的规则之一,它同时承担了三个职责:

  1. 封禁不安全的"空对象"类型{}
  2. 封禁危险或具有误导性的内置类型,例如FunctionNumber等;
  3. 允许用户自定义额外的封禁类型名单。

这三个目标都是很好的 lint 方向,但把三者揉进同一条规则带来了明显的设计问题(详见官方博客 Revamping theban-typesrule):

  • 难以按需配置:由于同时覆盖三个领域,用户无法只针对自己项目需要的部分进行轻量配置;
  • {}过于严苛{}的语义在何时该用、何时不该用上有很强的"灰度",这种微妙之处无法用简单的规则配置格式表达,导致大量误报和困惑;
  • 默认封禁类型修复能力有限:对默认封禁的内置类型,能提供的自动修复和边界情况处理非常有限。

这一问题的根源可以追溯到 TSLint。最早的"封禁类型"规则来自 TSLint 的ban-types,它默认不封禁任何类型,只封禁用户在配置里显式声明的类型。当该规则移植到@typescript-eslint/ban-types时,改为默认额外封禁一批已知危险的内置类型,这样plugin:typescript-eslint/recommended预设配置就能默认开启这些检查。副作用是,像object{}这种需要精细区分的类型,也被塞进了和Function一样的简单配置通道里。

因此,typescript-eslint v8 将旧的ban-types规则拆分为多个更聚焦的规则,ban-types本身在 v8 中被移除。官方文档明确保留ban-types页面(ban-types.md)的唯一目的,就是为了把用户引导到替代规则上——由于该页面不参与正常导航,只能通过搜索框进入。

二、迁移总览:一个规则变成四个

ban-types的功能被拆解为四份,分别对应四个新规则:

ban-types职责新规则定位是否默认开启
封禁用户自定义的任意类型名no-restricted-types纯用户配置,默认无任何封禁否(仅all预设)
封禁令人困惑的内置{}空对象类型no-empty-object-type默认封禁,提供建议修复是(recommended
封禁不安全的Function内置类型no-unsafe-function-type默认封禁,无配置项是(recommended
封禁ObjectNumber等内置包装类型no-wrapper-object-types默认封禁,提供自动修复是(recommended

其中no-restricted-types的行为与 ESLint 核心规则中的no-restricted-globals与 flat 版 recommended 配置 中,no-empty-object-typeno-unsafe-function-typeno-wrapper-object-types均以'error'开启,而no-restricted-types只出现在all预设(eslintrc/all.ts、flat/all.ts)中,且同样为'error'

三、no-restricted-types:用户自定义封禁类型名单

no-restricted-types是四个新规则中与旧ban-types用户自定义能力最接近的规则,用于封禁特定的类型注解。典型场景是项目正在从某个类型迁移到另一个类型,希望禁止对旧类型的引用。需要注意的是,该规则只封禁类型层面的引用,并不封禁对应的运行时对象

3.1 可封禁的类型形式

被封禁的类型既可以是类型名字面量(如OldType),也可以是带泛型参数实例化的类型名(如OldType<MyArgument>)。配置值支持三种形态:

  • 字符串:作为命中该类型时展示的错误消息;
  • 对象,包含以下可选属性:
    • message: string:类型被命中时展示的消息;
    • fixWith?: string:运行自动修复时用来替换封禁类型的字符串,省略则不做修复;
    • suggest?: string[]:提供给用户的建议替换列表(非自动修复,由编辑器提示用户选择)。
  • 布尔值true:使用默认消息封禁该类型(源码 schema 中允许true,见 no-restricted-types.ts)。

3.2 完整配置示例

{ "@typescript-eslint/no-restricted-types": [ "error", { "types": { // 自定义消息,解释为什么不该用它 "OldType": "Don't use OldType because it is unsafe", // 自定义消息 + 告诉插件如何自动修复 "OldAPI": { "message": "Use NewAPI instead", "fixWith": "NewAPI", }, // 自定义消息 + 提供建议修复(由用户在编辑器中选择) "SoonToBeOldAPI": { "message": "Use NewAPI instead", "suggest": ["NewAPIOne", "NewAPITwo"], }, }, }, ], }

该规则的源码还会对配置中的类型名做去空格处理(removeSpaces移除所有空白字符),并对代码中实际出现的类型名做同样的归一化后再匹配,这意味着OldAPI与代码中写作OldAPI <string>这类含空格的写法也能被正确识别(见 no-restricted-types.ts)。

3.3 源码层面的检查范围

从 no-restricted-types.ts 的监听器可以看出,规则覆盖了以下 AST 节点:

  • TSTypeReference:普通类型引用(含带类型实参的写法);
  • TSClassImplements/TSInterfaceHeritageimplementsextends子句;
  • TSTypeLiteralTSTupleType:空对象字面量类型{}与空元组[]
  • 内置类型关键字(bigintbooleannumberstringsymbolunknownvoid等,见 TYPE_KEYWORDS 映射):仅当配置中显式封禁了对应关键字时才挂载监听器。

对命中的节点,规则会报告Don't use \{{name}}` as a type.{{customMessage}};配置了fixWith时提供自动修复(fixer.replaceText将节点整体替换),配置了suggest时提供Replace `{{name}}` with `{{replacement}}`的建议修复。注意自动修复是可以被--fix直接应用的,因此官方在 schema 描述中特别提醒fixWith` 要谨慎使用。

3.4 何时不该用

如果你没有"封禁特定类型"的需求,就不需要这条规则。

四、no-empty-object-type{}空对象类型的迷惑与替代

{}("空对象"类型)是 TypeScript 结构化类型体系里最容易让新手困惑的写法。{}表示任意"非 nullish"值,包括字面量0""

let anyNonNullishValue: {} = 'Intentionally allowed by TypeScript.';

也就是说,{}真正的含义是"任意被定义的值"——包括数组、类实例、函数以及stringsymbol等原始类型。因此,开发者写{}时通常想表达的是:

  • object:表示任意对象值;
  • unknown:表示包括nullundefined在内的任意值。

为避免这种语义混淆,no-empty-object-type默认封禁{}类型的使用,涵盖无字段的接口声明和空的对象类型别名

4.1 正确与错误示例

❌ 错误写法:

let anyObject: {}; let anyValue: {}; interface AnyObjectA {} interface AnyValueA {} type AnyObjectB = {}; type AnyValueB = {};

✅ 正确写法:

let anyObject: object; let anyValue: unknown; type AnyObjectA = object; type AnyValueA = unknown; type AnyObjectB = object; type AnyValueB = unknown; let objectWith: { property: boolean }; interface InterfaceWith { property: boolean; } type TypeWith = { property: boolean };

4.2 规则默认放行的两种情况

该规则刻意不报告以下两种场景(这正是旧ban-types配置格式无法表达、而新规则内建豁免的边界情况):

  • 作为交叉类型成员的{}(例如 TypeScript 内置的type NonNullable<T> = T & {}),它在类型系统运算中是有用且合理的;
  • 继承自多个其他接口的接口(interface A extends B, C {}这类没有新增字段的空接口)。

4.3 选项详解

该规则默认同时检查空接口与空对象类型(allowInterfacesallowObjectTypes默认均为'never',见 no-empty-object-type.ts)。

allowInterfaces可取值:

  • 'always':始终允许无字段的接口;
  • 'never'(默认):从不允许无字段的接口;
  • 'with-single-extends':允许仅继承单个基接口的空接口。

{ allowInterfaces: 'with-single-extends' }下的正确代码:

interface Base { value: boolean; } interface Derived extends Base {}

allowObjectTypes可取值:

  • 'always':始终允许无字段的对象字面量类型;
  • 'never'(默认):从不允许。

allowWithName:一个字符串形式的正则表达式,用于按名字放行空接口/空对象类型别名。如果你的既有代码风格习惯用{}而非object声明空类型,这个选项会很有用。例如{ allowWithName: 'Props$' }

❌ 仍会报错:

interface InterfaceValue {} type TypeValue = {};

✅ 被放行:

interface InterfaceProps {} type TypeProps = {};

从源码看,allowWithName会编译为带u标志的正则(new RegExp(allowWithName, 'u')),同时作用于接口名(TSInterfaceDeclaration)和类型别名(TSTypeAliasDeclaration包装的空TSTypeLiteral)。此外,源码对空接口还有一个额外豁免:如果该名字与类声明或另一个接口声明合并(mergedWithOtherDeclaration,即声明合并场景)或为默认导出,则不提供自动替换建议,避免破坏既有代码结构(见 no-empty-object-type.ts)。

该规则属于suggestion类型,会针对空接口/空对象分别给出替换为objectunknown的建议修复:对空对象直接fixer.replaceText替换节点;对空接口则将其改写为type X = object | unknown形式的类型别名(源码中的replaceEmptyInterface修复逻辑,no-empty-object-type.ts)。

4.4 何时不该用

如果你的代码经常需要表示"任意非 nullish 值",或者大量使用条件类型、映射类型等类型运算,该规则可能不适合你的项目,可以考虑关闭。若确实有 API 需要接收{},官方建议通过配置规则选项、使用 ESLint 禁用注释或直接在 ESLint 配置中关闭规则来处理。另外,与该规则配套的还有no-generated-empty-object-type,它负责报告经类型运算解析后得到{}的情况,而本规则只处理手写的{}

五、no-unsafe-function-type:封禁危险的Function类型

TypeScript 内置的Function类型允许以任意数量的参数调用,且返回类型是anyFunction还允许恰好具备Function类全部属性的类或普通对象被赋值。这意味着Function基本抹掉了所有函数签名的类型安全,应尽量用函数类型语法明确参数与返回值类型。

5.1 正确与错误示例

❌ 错误写法:

let noParametersOrReturn: Function; noParametersOrReturn = () => {}; let stringToNumber: Function; stringToNumber = (text: string) => text.length; let identity: Function; identity = value => value;

✅ 正确写法(可参考的"兜底"函数类型):

let noParametersOrReturn: () => void; noParametersOrReturn = () => {}; let stringToNumber: (text: string) => number; stringToNumber = text => text.length; let identity: <T>(value: T) => T; identity = value => value;

官方文档还给出了两种常见的"兜底/全捕获"函数类型供参考:

  • () => void:无参数、返回值被忽略的函数;
  • (...args: never) => unknown:函数的"top type",可赋值给任意函数类型,但本身不可被调用。

5.2 源码实现要点

该规则是一个problem型、无任何配置项的规则。在 no-unsafe-function-type.ts 中,规则通过isReferenceToGlobalFunction('Function', node, context.sourceCode)判断标识符是否真正指向全局内置Function类型——这意味着如果你在作用域内自行声明了同名类型或变量,规则不会误报。检查覆盖TSClassImplementsTSInterfaceHeritageTSTypeReference三类节点。报告消息为:The \Function` type accepts any function-like value. Prefer explicitly defining any function parameters and return type.`

5.3 何时不该用

如果项目还处于 TypeScript 迁移初期,短期内难以把全部不安全的Function类型替换为精确的函数类型,可以考虑在个别场景使用 ESLint 禁用注释,而不是整体关闭规则。

六、no-wrapper-object-types:封禁大小写混淆的包装对象类型

TypeScript 定义了若干组"看起来很像、实则含义完全不同"的类型对:boolean/Booleannumber/Numberstring/Stringbigint/BigIntsymbol/Symbolobject/Object。一般来说只应使用小写变体,本规则强制这一点。

6.1 为什么必须用小写

JavaScript 在运行时只有 8 种数据类型,对应 TypeScript 的小写类型:undefinednullbooleannumberstringbigintsymbolobject。而大写类型是结构化类型,描述的是各数据类型的 JavaScript"包装"对象(如BooleanNumber)。由于结构化类型的"形状相同"怪癖,对应原始类型也能赋值给这些大写类型(let myObject: Object = 'allowed by TypeScript';能通过编译)。在运行时,包装对象与原始值的行为差异巨大:

  • 相等性:原始值按值比较("str" === "str"),包装对象按引用比较(new String("str") !== new String("str"));
  • 真值性:原始值有大家依赖的真假值规则,而所有对象恒为真——即使new Boolean(false)也为真;
  • 运算限制:TypeScript 只允许对数值原始类型进行算术运算(如x - y),不允许对对象进行。

因此用number而非Number才能更准确地描述代码。这是普遍的最佳实践:直接使用0这样的原始值,而非"长得像原始值"的对象new Number(0)

6.2 正确与错误示例

❌ 错误写法:

let myBigInt: BigInt; let myBoolean: Boolean; let myNumber: Number; let myString: String; let mySymbol: Symbol; let myObject: Object = 'allowed by TypeScript';

✅ 正确写法:

let myBigint: bigint; let myBoolean: boolean; let myNumber: number; let myString: string; let mySymbol: symbol; let myObject: object = "Type 'string' is not assignable to type 'object'.";

6.3 源码实现要点

该规则是problem型规则,fixable: 'code'。在 no-wrapper-object-types.ts 中,被检查的类名集合固定为BigIntBooleanNumberObjectStringSymbol六个;与no-unsafe-function-type相同,也通过isReferenceToGlobalFunction确认是全局内置引用后才报告。规则报告Prefer using the primitive \{{preferred}}` as a type name, rather than the upper-cased `{{typeName}}`.,其中preferred` 即小写化后的类型名。

自动修复逻辑(no-wrapper-object-types.ts)有一个细节值得注意:对于TSTypeReference(普通类型注解位置)提供自动修复(fixer.replaceText替换为小写),而对于TSClassImplementsTSInterfaceHeritageimplements/extends子句)则不提供修复——因为在继承/实现位置直接改成小写类型会改变语义。

6.4 何时不该用

如果你的项目极少数情况下确实需要处理原始类型的类等价物,可以仅在那些具体位置使用禁用注释,而不是整体关闭规则。

七、升级迁移路径:从ban-types到新规则

7.1 直接迁移策略

由于三个默认封禁规则(no-empty-object-typeno-unsafe-function-typeno-wrapper-object-types)已包含在recommended预设中,使用plugin:typescript-eslint/recommended(或 flat config 的tseslint.configs.recommended)的项目升级到 v8 后会自动获得原ban-types默认封禁行为,无需额外配置。

对于在旧ban-types中自定义封禁的类型名单,需要手动迁移到no-restricted-typestypes配置中。对照关系如下:

ban-types写法no-restricted-types写法
"types": { "OldType": "自定义消息" }"types": { "OldType": "自定义消息" }
字符串消息字符串消息(不变)
message对象message对象(不变)
fixWith自动替换fixWith自动替换(新增于旧版之上)
suggest建议列表(新规则独有能力)

从配置语义上看,旧规则的用户自定义部分基本是"平移"到新规则即可,而新规则额外带来了suggest(编辑器内建议修复)能力——适用于不确定修复是否可靠、不想让--fix自动应用的场景。官方博客在 Revamping theban-typesrule 中也给出了一个"封禁旧 API 并提供两个建议"的典型配置:

{ "@typescript-eslint/no-restricted-types": [ "error", { "types": { "DeprecatedOldAPI": { "message": "Use either NewAPIOne or NewAPITwo instead", "suggest": ["NewAPIOne", "NewAPITwo"], }, }, }, ], }

7.2 依赖项检查

由于ban-types在 typescript-eslint v8 中被移除,升级前请检查项目中是否还直接引用了@typescript-eslint/ban-types规则名,并将其替换为上表中的四个新规则。官方在 v8 相关发布说明(Announcing typescript-eslint v8 Beta、Announcing typescript-eslint v8)中列出了全部破坏性变更,建议升级前通读。若仍需历史背景与拆分细节,可阅读仓库内保留的 ban-types.md 与 Revamping theban-typesrule。

八、四个新规则的测试覆盖

四个新规则在仓库中均有完整的测试套件,可当作理解规则行为的补充材料:

  • no-restricted-types.test.ts:覆盖自定义消息、fixWith自动修复、suggest建议、泛型类型名匹配、关键字类型封禁等场景;
  • no-empty-object-type.test.ts:覆盖allowInterfaces三种取值、allowObjectTypesallowWithName正则、声明合并豁免等场景;
  • no-unsafe-function-type.test.ts:覆盖全局Function识别与局部同名遮蔽等场景;
  • no-wrapper-object-types.test.ts:覆盖六个大写包装类型、自动修复、implements子句不修复等场景。

九、总结与决策建议

ban-types从"一个规则管三件事"重构为"四条单一职责规则",是 typescript-eslint v8 中一次典型的"按关注点拆分"演进。迁移时只需记住一条主线:默认封禁交给推荐配置,自定义封禁交给no-restricted-types

  • 使用recommended(或strict)预设的用户,升级 v8 后{}Function、包装类型三类默认封禁自动生效;
  • 曾自定义ban-types.types的用户,把配置平移进no-restricted-types,并可按需利用新增的suggest建议修复;
  • 需要精细控制空接口/空对象类型的用户,通过no-empty-object-typeallowInterfacesallowObjectTypesallowWithName三个选项即可覆盖绝大多数边界场景,这些精细化选项正是旧ban-types无法提供的。

通过本文的配置示例与源码分析,你可以在升级到 typescript-eslint v8 后,快速完成ban-types相关配置的迁移,并借助新规则更精细的能力治理代码中的危险类型用法。

【免费下载链接】typescript-eslint:sparkles: Monorepo for all the tooling which enables ESLint to support TypeScript项目地址: https://gitcode.com/GitHub_Trending/ty/typescript-eslint

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/14 9:51:40

700行手写RTOS内核:Cortex-M任务调度与临界区原理实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/14 9:50:48

N皇后II优化全解析:从回溯到位运算与对称剪枝

刷过LeetCode的读者对第51题N皇后肯定不陌生&#xff0c;输出棋盘布局的回溯解法几乎是每个算法学习者的入门必修课。但紧接着的第52题N皇后II&#xff0c;很多人只是把它当成同一道题的简化版——只要把保存结果的代码删掉、改成计数器加一就行&#xff0c;于是草草收场。真正…

作者头像 李华
网站建设 2026/9/14 9:49:03

WeMod免费版时长限制挡路?Wand-Enhancer本地补丁免费解锁Pro

WeMod免费版时长限制挡路&#xff1f;Wand-Enhancer本地补丁免费解锁Pro 【免费下载链接】Wand-Enhancer Advanced UX and interoperability extension for Wand (WeMod) app 项目地址: https://gitcode.com/GitHub_Trending/we/Wand-Enhancer 打boss打到一半&#xff0…

作者头像 李华
网站建设 2026/9/14 9:45:45

配电网有功无功协调优化:光伏不确定性建模与二阶锥松弛求解

简介&#xff1a;面向电力系统自动化及相关专业毕业设计的Matlab仿真源码包&#xff0c;针对分布式光伏接入配电网后潮流方向不确定性改变、节点电压越限风险&#xff0c;提出光伏无功出力与静止无功发生器&#xff08;SVG&#xff09;协调控制策略&#xff0c;并以网损、电压偏…

作者头像 李华