背景
当我们尝试在一个大型 TS 历史项目中,开启 TS 校验来预防一些低级语法错误时,通常会被遗留的大量 TS 错误阻塞住,这时一般有两种选择:
-
修复所有错误(理想情况,但非常耗时)。
-
使项目可编译,并逐步修复错误(推荐)。
对于第二种方案,我们可以通过自动化脚本来实现。使用 ts-migrate,它会启动一个 tsserver 将代码中存在错误的地方标记出来并注释掉,使用起来也很方便。可类比为 vscode 里 ts 插件启动的 tsserver,提供了飘红标记,提示错误的能力。
这样项目至少可以通过 TS 编译先跑起来,在后续开启 CI 卡点之后,可以有效防止新增错误的合入。除此之外,这个工具还有一些其他功能,例如批量转换 js 文件,更多内容可前往 Github 了解。
迁移指令
执行命令:
ts-migrate reignore <project path>
运行结果:
function operationPermission(): UserPermission {
// @ts-expect-error TS(2339) FIXME: Property 'userPermission' does not exist on type '... Remove this comment to see the full error message
return this.options?.userPermission;
}
@ts-expect-error vs @ts-ignore
也许你注意到了,工具默认使用@ts-expect-error注释代码,而非@ts-ignore,它们有一些区别。
@ts-expect-error 告诉 TypeScript 预期下一行代码会有一个错误,如果没有错误,它会抛出一个错误,提醒你移除注释——这便是所谓的“渐进式修复”,非常适合治理我们这种历史包袱重的场景。
error TS2578: Unused '@ts-expect-error' directive
而@ts-ignore仅仅告诉编译器忽略特定代码,相比于@ts-expect-error显得过于绝对,并且大概率之后不会有人再去管它了🌚。
因此,工具默认使用@ts-expect-error还是比较合理的。
FAQ
在 JSX 组件上下文中,注释插入位置有误
<div className={style.inputField} style={props.style}>
// @ts-expect-error ts-migrate(2607) FIXME: JSX element class does not support attributes beca... Remove this comment to see the full error message
<RawInput className={style.input} {...props} />
</div>
这种写法,注释实际上变成了文本节点,会被渲染出来,不符合要求,正确的写法应该是使用花括号包裹{/* @ts-expect-error ... */}
,目前这是一个已知问题,详见 issue——ts-migrate#150。
可以通过降级 typescript 依赖版本到 4.7.2 临时解决这一问题。
有很多 unused @ts-expect-error 编译报错
理论上不应该出现这样的情况,但实践下来,确实会存在少部分代码被多加了注释,从而导致编译时错误。
src/demo.ts(1:1): error TS2578 unused @ts-expect-error directive
为此,我编写一个小工具:emosheeep/commover,可以根据 TS 输出的编译日志,解析出需要删除的行,快速清除这些注释(通常这些注释都是单独一行)。