TypeScript Automated Migration for Breaking Changes to the Type System

bzzcjhmw  于 4个月前  发布在  TypeScript
关注(0)|答案(7)|浏览(42)

自动迁移以解决类型系统的重大变更

搜索词

codemod, 迁移, 升级

建议

在类型系统发生重大变更时进行半自动化迁移
一种实现方式是通过 codemods(可以使用 TS 编译器 API)来执行如 #33272 中描述的简单更新任务。

用例

  • 使用户更容易升级到最新的 TypeScript
  • 使 TypeScript 团队更容易升级 TypeScript
  • 可能使 TS 更快地进化,因为这些“破坏性”更改所带来的痛苦会更小

例如,#33272 中描述的更改大部分都是微不足道的,理想情况下不需要太多人工干预。
仅针对类型的更改尤其适合这种处理方式,因为如果转换中存在错误,那么没有人的应用会在运行时出现问题。
我说“半自动化”是因为在工具无法确定问题的情况下,工具可以退让给人类判断。为此提供的工具不必完美,但一小部分自动化可以发挥很大作用。

示例

example.ts

const myPromise: Promise<{}> = dontCarePromise();

tsc --upgrade --from=3.5 --to=3.6 example.ts

const myPromise: Promise<unknown> = dontCarePromise();

示例来自 #33272
在转换 可能会导致新的类型错误 的情况下,我希望工具会退出,解释情况,并让人类决定如何处理。

检查清单

我的建议符合以下准则:

  • 这不会对现有的 TypeScript/JavaScript 代码造成破坏性的更改
  • 这不会改变现有 JavaScript 代码的运行时行为
  • 这可以在不根据表达式的类型生成不同的 JS 的情况下实现
  • 这不是一个运行时特性(例如库功能、带有 JavaScript 输出的非 ECMAScript 语法等)
  • 这个特性将与 TypeScript's Design Goals 中的其他内容保持一致。
bkkx9g8r

bkkx9g8r1#

这很酷,我也想看到同样的按国家/地区划分的数据,例如 tsc --upgrade-flag strictFunctionTypes example.ts

x8goxv8g

x8goxv8g2#

如果我们认为这是可能的并且有用的,我们确实为编辑器构建了这些类型的快速修复(尽管我们仍然没有一个可以从命令行运行修复的工具@DanielRosenwasser )。我们只是不总是费心去麻烦 - unknown 默认更改只需要在DT更新时大约20个包(每个包在一个或两个地方,实际上在一些情况下指出了一个未处理的空/未定义的错误),所以看起来并不那么糟糕。

db2dz4w8

db2dz4w83#

我想说的是:我认为#13195需要自动化迁移。然而,与该问题相关的还有其他多个阻塞功能。

js81xvg6

js81xvg64#

Should I close this issue, given that this sort of thing is in the works already per Wes' comment? #33345 (comment)

5hcedyr0

5hcedyr05#

我的意思是,我会把这个问题留给@DanielRosenwasser,因为他一直在寻找一个理由来构建命令行上的快速修复功能。

8ulbf1ek

8ulbf1ek6#

大致上有两种不同类型的错误需要考虑:

  • 根本原因可以通过类型系统轻易识别的错误(类型I)
  • 根本原因无法通过类型系统轻易识别的错误(类型II)

一个类型I错误的示例是 strictClassPropertyInitialization - 通过错误本身的结构,TS可以轻松地识别出“我正在发出一个错误,原因是添加了一个以破坏性方式进行修改的规则”。
一个类型II错误的示例是在 #33272 中识别出的 Set 构造函数更改 - 这是由于推理规则中的微妙变化引起的,而代码中稍后发生的确切错误并不是TS可以合理地追溯到行为更改的原因。
我们可以为类型I错误创建快速修复程序,通常也会这样做。这些修复程序也往往具有很好的文档记录和经过深思熟虑的更改,因为它们通常是“我们故意破坏你”的那种破坏性更改。许多类型I错误还与关闭它们的命令行标志相关联。
对于类型II错误,即使我们能弄清楚发生了什么,通常也没有“快速修复”,因为它取决于代码最初试图做什么,而需要发生的更改可能在第一个实际类型错误发出的地方非常词汇上远离。
我们可以整天提供针对类型I错误的工具,但我认为这是一种低价值活动。这些错误通常并不特别痛苦,提前得到很好的信号,通常有一个简单的命令行修复或可以通过足够聪明的正则表达式批量解决。采用类似Facebook的 codemod 工具为这些修复提供CLI体验可能是值得考虑的事情。
类型II错误令人烦恼。我们甚至可能根本不知道自己正在制造它们 - filter(Boolean) 再次来自 #33272 就是个例子。即使我们完全意识到这将是一个破坏性更改,也不清楚我们会怎么做。没有明显的快速修复来发布“这个表达式将以一种可能会破坏下游代码的方式改变为更准确的类型,从而可能破坏做出某些其他假设的代码”的错误。
例如,假设您编写了一些代码

const arr = [1, 2, 3, "four", "five"];
const arr2 = arr.filter(x => +x === x);
arr2.push("six");

也许将来,TS变得更聪明,说 +x === xx is number 的类型保护表达式,并且通过识别 array.filter(type guard function) 返回更具体的数组类型变得更聪明。乍一看,这不是一个破坏性更改...但是它是:arr2.push("ok") 行现在是一个错误,因为 arr.filter 返回了 number[] 而不是 (string | number)[]
即使是最简单的例子,错误与原始更改之间的因果关系也非常脱节,甚至不清楚发布正确的“修复”是什么。
对于类型II错误,似乎您只需要一个运行 tsc 的工具,接收错误并插入Assert或 // @ts-ignore 注解即可。例如,根据错误的密度,我可能会有以下两种输出之一:

const arr = [1, 2, 3, "four", "five"];
const arr2 = arr.filter(x => +x === x);
// @ts-ignore Error 'cannot convert "six" to 'number'" introduced in TS 4.2
arr2.push("six");

vs

const arr = [1, 2, 3, "four", "five"];
// TODO: Multiple errors involving 'arr2' introduced in TS 4.2
const arr2: any = arr.filter(x => +x === x);
arr2.push("six");
arr2.push("seven");
arr2.push("eight");
edqdpe6u

edqdpe6u7#

我们可以整天为类型I断点提供工具,但我认为这是一项低价值活动。这些断点通常并不特别痛苦,提前得到很好的信号,通常可以通过简单的命令行修复或足够聪明的正则表达式批量解决。

我认为快速修复,在很多时候,往往是相当低投入的,并有助于更多地教育用户关于断点本身以及如何修复它们。所以我不认为它是低价值的,即使它是,最初的努力也不是不合理的。

相关问题