ARTICLE DETAIL

资讯详情

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

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

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-types、no-empty-object-type、no-unsafe-function-type、no-wrapper-object-types每个新规则的具体配置方法、源码实现原理、默认启用情况以及如何把旧的ban-types配置平滑迁移到新规则让升级到 typescript-eslint v8 不再困惑。一、背景ban-types规则为何被拆掉在 typescript-eslint 的早期版本中typescript-eslint/ban-types是最具代表性的规则之一它同时承担了三个职责封禁不安全的空对象类型{}封禁危险或具有误导性的内置类型例如Function、Number等允许用户自定义额外的封禁类型名单。这三个目标都是很好的 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封禁Object及Number等内置包装类型no-wrapper-object-types默认封禁提供自动修复是recommended其中no-restricted-types的行为与 ESLint 核心规则中的no-restricted-globals与 flat 版 recommended 配置 中no-empty-object-type、no-unsafe-function-type、no-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也可以是带泛型参数实例化的类型名如OldTypeMyArgument。配置值支持三种形态字符串作为命中该类型时展示的错误消息对象包含以下可选属性message: string类型被命中时展示的消息fixWith?: string运行自动修复时用来替换封禁类型的字符串省略则不做修复suggest?: string[]提供给用户的建议替换列表非自动修复由编辑器提示用户选择。布尔值true使用默认消息封禁该类型源码 schema 中允许true见 no-restricted-types.ts。3.2 完整配置示例{ typescript-eslint/no-restricted-types: [ error, { types: { // 自定义消息解释为什么不该用它 OldType: Dont 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/TSInterfaceHeritageimplements与extends子句TSTypeLiteral与TSTupleType空对象字面量类型{}与空元组[]内置类型关键字bigint、boolean、number、string、symbol、unknown、void等见 TYPE_KEYWORDS 映射仅当配置中显式封禁了对应关键字时才挂载监听器。对命中的节点规则会报告Dont 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.;也就是说{}真正的含义是任意被定义的值——包括数组、类实例、函数以及string、symbol等原始类型。因此开发者写{}时通常想表达的是object表示任意对象值unknown表示包括null和undefined在内的任意值。为避免这种语义混淆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 NonNullableT T {}它在类型系统运算中是有用且合理的继承自多个其他接口的接口interface A extends B, C {}这类没有新增字段的空接口。4.3 选项详解该规则默认同时检查空接口与空对象类型allowInterfaces、allowObjectTypes默认均为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类型会针对空接口/空对象分别给出替换为object或unknown的建议修复对空对象直接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类型允许以任意数量的参数调用且返回类型是any。Function还允许恰好具备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类型——这意味着如果你在作用域内自行声明了同名类型或变量规则不会误报。检查覆盖TSClassImplements、TSInterfaceHeritage与TSTypeReference三类节点。报告消息为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/Boolean、number/Number、string/String、bigint/BigInt、symbol/Symbol、object/Object。一般来说只应使用小写变体本规则强制这一点。6.1 为什么必须用小写JavaScript 在运行时只有 8 种数据类型对应 TypeScript 的小写类型undefined、null、boolean、number、string、bigint、symbol、object。而大写类型是结构化类型描述的是各数据类型的 JavaScript包装对象如Boolean、Number。由于结构化类型的形状相同怪癖对应原始类型也能赋值给这些大写类型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 中被检查的类名集合固定为BigInt、Boolean、Number、Object、String、Symbol六个与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替换为小写而对于TSClassImplements与TSInterfaceHeritageimplements/extends子句则不提供修复——因为在继承/实现位置直接改成小写类型会改变语义。6.4 何时不该用如果你的项目极少数情况下确实需要处理原始类型的类等价物可以仅在那些具体位置使用禁用注释而不是整体关闭规则。七、升级迁移路径从ban-types到新规则7.1 直接迁移策略由于三个默认封禁规则no-empty-object-type、no-unsafe-function-type、no-wrapper-object-types已包含在recommended预设中使用plugin:typescript-eslint/recommended或 flat config 的tseslint.configs.recommended的项目升级到 v8 后会自动获得原ban-types默认封禁行为无需额外配置。对于在旧ban-types中自定义封禁的类型名单需要手动迁移到no-restricted-types的types配置中。对照关系如下旧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三种取值、allowObjectTypes、allowWithName正则、声明合并豁免等场景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-type的allowInterfaces、allowObjectTypes、allowWithName三个选项即可覆盖绝大多数边界场景这些精细化选项正是旧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),仅供参考
返回列表