☰
typescript-eslint 的 `rule-schema-to-typescript-types` 包:从 ESLint 规则 Schema 生成 TypeScript 类型定义
2026/10/11 6:03:35 网站建设 项目流程
  • 开发工具
  • 静态分析
  • Lint
  • 代码质量

【免费下载链接】typescript-eslint

:sparkles: Monorepo for all the tooling which enables ESLint to support TypeScript

项目地址:https://gitcode.com/GitHub_Trending/ty/typescript-eslint
点击查看免费下载

@typescript-eslint/rule-schema-to-typescript-types是 typescript-eslint 仓库中的一个内部工具包,它读取规则在meta.schema中声明的 JSON Schema(v4),并输出与之等价的 TypeScript 类型字符串(形如type Options = [...])。这些生成结果既用于文档展示,也作为规则类型定义的权威来源。读完本文,你将理解该包的输入/输出约定、类型映射规则、$ref/$defs处理方式、错误边界与优化策略,并能独立复现其核心能力。

一、包定位:内部工具,服务于文档与类型生成

在 typescript-eslint 这个 monorepo 中,packages/rule-schema-to-typescript-types/README.md 明确声明这是一个Internal Package(内部包),面向 typescript-eslint 仓库自身的工具链,而非面向最终用户的独立产品。官方文档页 docs/packages/RuleSchemaToTypeScriptTypes.mdx 对它的描述是一句话:

Converts ESLint rule schemas to equivalent TypeScript type strings ✨

包的package.json(packages/rule-schema-to-typescript-types/package.json)中 version 为8.71.1,依赖了同仓库的@typescript-eslint/type-utils与@typescript-eslint/utils,另外依赖natural-compare(用于排序,保证输出确定性),Node 版本要求^18.18.0 || ^20.9.0 || >=21.1.0。

该包在整个仓库中的核心消费方是 eslint-plugin 的类型生成测试 packages/eslint-plugin/tests/schemas.test.ts:它遍历所有规则的ruleDef.meta.schema,调用schemaToTypes生成类型字符串,再用 Prettier 格式化后写入schema-snapshots/*.shot快照文件。也就是说,每一个规则的"可读类型签名"都来自这个包。对应的快照产物可见 packages/eslint-plugin/tests/schema-snapshots/(例如no-unused-vars.shot),其中# SCHEMA:段是原始 JSON Schema,# TYPES:段是生成的类型定义。

二、入口函数schemaToTypes:输入与输出约定

对外唯一入口是schemaToTypes,实现在 src/index.ts:

export function schemaToTypes( schema: JSONSchema4 | readonly JSONSchema4[], ): string

关键行为:

  • 输入:可以是单个 JSON Schema v4 对象,也可以是 schema 数组。JSONSchema4类型定义在 packages/utils/src/json-schema.ts(对应 JSON Schema draft v4 的各个子类型:JSONSchema4ObjectSchema、JSONSchema4ArraySchema、JSONSchema4RefSchema等)。
  • 数组输入 → 元组类型:数组被当作规则 options 的元组,输出type Options = [T0, T1, ...];
  • 对象输入 → 单一类型:输出type Options = T;
  • 空数组输入:返回固定的/** No options declared */+type Options = [];(对应测试 tests/index.test.ts 的第一条用例);
  • $defs/definitions展开:仅支持顶层的$defs或definitions(源码注释we only support defs at the top level for simplicity),每个定义键会被转换为 PascalCase 类型名(toPascalCase,首字母大写),并同时注册#/$defs/{key}与#/items/{index}/$defs/{key}两个引用路径,随后以"引用类型在前、Options 类型在后、块间空行分隔"的顺序拼接输出。

官方文档页 docs/packages/RuleSchemaToTypeScriptTypes.mdx 给出的最小示例:

import { schemaToTypes } from '@typescript-eslint/rule-schema-to-typescript-types'; schemaToTypes({ description: 'My great option!', items: { type: 'string' }, type: 'array', }); // 输出: // type Options = [ // /** My great option! */ // string[] // ];

三、类型生成规则:JSON Schema → TS 类型的映射

核心递归逻辑在 src/generateType.ts,处理顺序如下:

  1. $ref优先:命中refMap则生成TypeReferenceAST(引用类型名);
  2. enum:把枚举值生成联合类型(见第四节);
  3. anyOf/oneOf:均生成联合类型。源码注释特别说明:anyOf在 JSON Schema 语义上其实是组合(T、U、V 的任意交集组合),但实践中大多被用来模拟oneOf,因此本工具直接按联合类型处理;
  4. type关键字:按类型分发——
    • any→unknown(不生成any,避免污染类型安全);
    • null→null;
    • number/string→ 直接输出对应关键字;
    • boolean→boolean;
    • integer→number(TS 无integer类型);
    • array→ 走generateArrayType(第五节);
    • object→ 走generateObjectType(第六节);
  5. 未声明type且不含$ref/enum/oneOf的 schema→ 抛出NotSupportedError("untyped schemas without one of [$ref, enum, oneOf]");
  6. type为数组(多类型 schema)→ 同样抛出NotSupportedError。

显式不支持的关键字

src/generateType.ts 定义了一组"可能应该支持但当前不支持"的关键字,命中即抛NotSupportedError:

allOf, dependencies, extends, maxProperties, minProperties, multipleOf, not, patternProperties

错误信息会带上完整 JSON 序列化的目标 schema,方便定位问题。

四、枚举与联合类型:generateUnionType

src/generateUnionType.ts 负责把枚举成员转为联合类型成员:

  • 字符串:转成单引号字面量(内部单引号会被转义为\');
  • 数字 / 布尔值:原样转成字面量;
  • 对象:递归走generateType;
  • 不支持的成员:null或数组出现在 enum 中会抛NotSupportedError。

联合成员在输出时会被排序以保证确定性(依赖natural-compare),排序逻辑见 src/printAST.ts 的compareElements:不同节点类型先按代码文本自然比较;同为 tuple 时先按元素数量升序(避免 natural-compare 把长元组排前面),再按代码比较。

五、数组与元组:generateArrayType的启发式策略

src/generateArrayType.ts 是策略最复杂的一环,核心规则:

  • items缺失:抛UnexpectedError({type: 'array'}理论上可映射为any[],但过于宽松,工具拒绝生成);
  • 单类型数组(items不是数组):直接生成T[];即使声明了minItems/maxItems也不生成元组(源码注释:[T, ...T[]]形式虽然可行,但为了文档可读性放弃,参见 issue #11117);
  • items为数组 → 按元组处理,并引入三个关键约束:
    • MAX_ITEMS_TO_TUPLIZE = 20:元组元素超过 20 个就不再元组化,直接退化为数组类型以保证简洁;
    • maxItems语义:只有当maxItems < 20时才考虑;若maxItems > items.length且存在additionalItems(对象形式),则生成展开元素(打印为...T[]);maxItems小于 items 个数、或大于 items 个数却没有additionalItems,都会抛UnexpectedError;
    • minItems语义:当 items 个数多于minItems时,不采用可选元素运算符[T, T?, T?](因为它允许['a', undefined, 'c']这种中间空洞),而是生成元组的联合[T] | [T, T] | [T, T, T],保证每个位置类型更精确(源码注释给出了这一类型安全论证),且只有联合的最后一个元组带展开参数。

例如测试中的用例[{ items: [{type:'string'}], type: 'array' }]输出为:

type Options = [ | [] | [string]]

六、对象类型:必填/可选属性与索引签名

src/generateObjectType.ts 处理type: 'object'的 schema:

  • 属性必填性:根据required数组判定;不在required中的属性输出为可选(?:)。required非数组时按空集合处理;
  • 属性名转义:通过@typescript-eslint/type-utils的requiresQuoting判断属性名是否需要加引号,必要时输出'prop-name'形式;
  • 索引签名:additionalProperties === true或未声明时,索引签名类型为unknown;additionalProperties为对象时递归生成其类型。输出形式为[k: string]: T;
  • 属性排序:在打印阶段(src/printAST.ts)用naturalCompare对属性名排序,保证"无论声明顺序如何,输出一致",并强制把对象打印为多行。

七、注释生成:description → JSDoc

src/getCommentLines.ts 将 schema 的description作为注释行;src/printAST.ts 的printComment负责排版:

  • 单行描述 →/** description */;
  • 多行 → 逐行加*前缀的多行 JSDoc;描述中的任意换行符(CRLF、CR、LF、行分隔符 U+2028、段落分隔符 U+2029)都会被ASTUtils.LINEBREAK_MATCHER拆分为独立注释行——测试 tests/index.test.ts 对五种换行符分别断言了多行注释输出。

八、AST 优化:联合展开与去重

src/optimizeAST.ts 在生成后对中间 AST 做一轮优化,主要是针对联合类型:

  • 递归优化所有子节点;
  • unwrapUnions展平嵌套联合,并把外层联合的注释行前置到第一个元素上,避免注释丢失;
  • 按JSON.stringify去重联合成员(注释注明这是"hacky way to deduplicate union members")。

九、打印输出:把 AST 渲染为类型字符串

src/printAST.ts 定义了完整的打印器:

  • printTypeAlias输出/** 注释 */\ntype 别名 = 类型;
  • 数组:T[],若元素是联合则加括号(T | U)[](printAndMaybeParenthesize);
  • 对象:{\nprop: T;\n[k: string]: T},属性间用分号连接并强制换行(注释说明这样能让 Prettier 稳定地按多行输出);
  • 元组:[T0,T1,...T[]],元素逐个打印(含各自注释),展开元素打印为...T[];
  • 联合:每个成员前加/** 注释 */ |,成员排序后按\n连接,整行以空格开头(因此快照中出现| 'a'的样式)。

十、错误边界:两种明确的异常

src/errors.ts 定义了两种错误,均携带完整的目标 schema JSON 以便排查:

  • NotSupportedError:遇到当前不支持的特性(如allOf、patternProperties、无类型且无$ref/enum的 schema、多类型数组、单类型数组配additionalItems、enum 中的null/数组);
  • UnexpectedError:遇到"不应发生"的内部不一致(如缺失items、maxItems与 items 数量矛盾、$ref找不到对应定义,错误信息会列出所有已注册的 ref 路径)。

十一、在仓库中的真实应用:schema-snapshots 工作流

eslint-plugin 的 schemas.test.ts 展示了完整的落地用法:

  1. 遍历../src/rules/index.js导出的全部规则;
  2. 将ruleDef.meta.schema用 Prettier 格式化后写入快照的# SCHEMA:段(对 enum 数组与对象属性做排序以保证跨平台稳定);
  3. 调用schemaToTypes(ruleDef.meta.schema),同样用 Prettier 格式化后写入# TYPES:段;
  4. 用toMatchFileSnapshot与 schema-snapshots/ 下的.shot文件比对,从而在 CI 中保证"schema 变更必须同步更新类型快照"。

以no-unused-vars.shot(packages/eslint-plugin/tests/schema-snapshots/no-unused-vars.shot)为例,其生成的类型(节选):

type Options = [ | 'local' | { /** Whether to check all, some, or no arguments. */ args?: | 'all' | 'none' | 'after-used'; argsIgnorePattern?: string; /** Whether to check catch block arguments. */ caughtErrors?: | 'none' | 'all'; caughtErrorsIgnorePattern?: string; // ... 其余属性 } | 'all', ];

可以看到:oneOf成员被展开为联合、enum 成员被排序、description被转成内联 JSDoc、非必填属性带?。这些快照同时是规则文档展示的素材来源,因此该包保证了"文档里看到的类型与规则实际校验的 schema 永远一致"。

十二、小结:何时使用与边界提醒

  • 使用场景:当你需要把 ESLint 规则的 options schema 转换为可读、可文档化的 TypeScript 类型时,schemaToTypes是开箱即用的工具;它也是 typescript-eslint 内部保证规则 schema 与类型文档一致性的基础设施。
  • 适用前提:输入必须是 JSON Schema draft v4(JSONSchema4),且避免使用本工具明确不支持的allOf、dependencies、patternProperties等关键字。
  • 已知取舍:anyOf按联合而非严格组合语义生成;maxItems > 20时不再元组化;单类型数组忽略minItems/maxItems;$defs/definitions仅支持顶层声明——这些都是为了文档可读性与实现简洁性做出的有意设计,使用前应了解这些边界。
  • 开发工具
  • 静态分析
  • Lint
  • 代码质量

【免费下载链接】typescript-eslint

:sparkles: Monorepo for all the tooling which enables ESLint to support TypeScript

项目地址:https://gitcode.com/GitHub_Trending/ty/typescript-eslint
点击查看免费下载
上一篇:unlazy深度解析:AI智能体完成度纪律神器,用可运行门控杜绝半截活
下一篇:codex-lb配额管理深度解析:搞懂5小时与周双窗口,榨干账号每一滴配额

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

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

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

立即咨询