我所理解的 TypeScript 最佳实践(5):类型检查、Lint 与测试
- 为什么选择 TypeScript
- 严格模式
- 状态、映射与泛型
- 模块边界与 TypeScript 脚本
- 类型检查、Lint 与测试(当前)
- TypeScript 7 升级与工具链兼容
一个 TypeScript 文件能运行、能通过类型检查、符合团队约定,是三件可以分别验证的事。比如组件运行测试能确认传入 mode="prompt" 会打开输入框,编译器还可以确认 mode="anything" 会被拒绝。
从项目第一轮就接入检查
在新项目的第一轮提交里,我会把格式检查、ESLint、类型检查和测试接进同一个 check 命令。Prettier 负责格式,tsc 或框架类型检查器负责静态契约,ESLint 处理可识别的代码风险和项目约定,测试覆盖实际运行行为。比如一个组件的 prop 要接受普通字符串,可以用类型测试覆盖合法值和非法值,再用组件测试验证传入合法值后真的会打开对应界面。只有需要保护「某种写法必须报错」的用例,才使用带原因的 @ts-expect-error。
有些规则只看语法就能执行,例如禁止跨模块导入私有实现。有些需要类型信息,例如判断 Promise 是否被妥善处理。后者要求 parser 能找到对应的 TypeScript 项目,脚本和测试也要匹配正确配置。已有通用规则时优先复用。稳定的项目约定可以放进共享插件,框架规则按文件或项目范围启用,启发式规则先观察真实误报。相关做法见把项目约定写成 ESLint 护栏和 typescript-eslint 的类型感知 Lint 指南。
让 Lint 看见新增的 JavaScript 文件
如果约定是「插件源码用 TypeScript」,只把 *.ts 传给 ESLint 会漏过刚加进去的 index.js。限制 import 路径里的 .js 后缀也检查不到这个文件,因为文件名和导入语句是两个不同的输入。
本站的 lint:plugins 会扫描插件里的 TS 和 JS 文件。对 .js、.mjs、.cjs、.jsx,ESLint 的 no-restricted-syntax 在 Program 节点报错,连空文件也能命中。dist/ 的文件由构建产生,按产物目录排除。迁移期间若某个入口必须临时保留,可以按完整路径记录原因,等它能从 TS 构建出来就移除例外。见 ESLint 的 no-restricted-syntax 规则。
可以临时在源码目录放一个 probe.js,用项目平时跑的命令检查退出码。如果它悄悄通过了,就先查扫描范围。再把同一个文件改为 TS,确认检查通过,最后让宿主加载生成产物。本站的工作区安装、预览和打包都调用 build:plugins,其中第一步就是运行这项检查。插件产物怎样生成见《模块边界与 TypeScript 脚本》。
升级时分别核对命令和编译器 API
新版 tsc 能跑类型检查时,typescript-eslint 仍可能依赖旧版编译器 API。如果当前版本组合需要并行安装,可以让检查命令和 ESLint 各自使用兼容的版本。这属于具体版本组合的验证。再升级时分别执行类型检查和真实 lint,并核对它们解析到哪个包。并行安装的配置、历史实测和当前版本见 TypeScript 7 升级与工具链兼容。