如何进行 TypeScript 转换

本文讨论了将JavaScript项目转换为TypeScript时,采用宽松模式(快速转换但后期逐步严格)与严格模式(一开始就严格)的优缺点。作者强调了严格模式在大型项目中的优势,尽管初期可能需要更多时间和精力,但长远来看有助于提高代码质量和团队协作效率。
摘要由CSDN通过智能技术生成

如何进行 TypeScript 转换

—— 符号多元数学,作者:Chris Krycho

假定受众 使用 JavaScript 和 TypeScript 的软件开发人员,或思考和使用其他语言的渐进类型系统。 特别是:我并不是支持 TypeScript 或 Python“类型”或 Ruby 的 Sorbet 等; 我正在和那些已经有兴趣收养它们的人交谈。

认知状态 我是 LinkedIn 在其数百万行库和应用程序中采用 TypeScript 的主要“主题专家” JavaScript 在过去六年中帮助 Ember.js 社区中的数十个团队采用了 TypeScript,并在 2017 年至 2018 年成功领导了 150,000 行代码的应用程序向严格类型化的 TypeScript 的转换。

我从有兴趣将 JavaScript 应用程序转换为 TypeScript 的人那里得到的最常见问题之一是:*我应该如何解决这个问题?*人们倾向于想到两种方法:

  • 一种相对宽松的方法:首先设置 compilerOptions.strict: false,在接触文件时转换文件,并通过启用各个严格标志来逐渐增加类型的稳健性,直到将它们全部打开 - 或这些的某种组合。
  • 更严格的方法:设置 compilerOptions.strict: true,并非常小心地以“叶子优先”的顺序转换代码库,在这种情况下,如果没有首先拥有其所有依赖项的类型,则不会转换任何模块。 明确“更严格”可能已经意味着什么:这是我首选的方法。

大多数开发人员(包括我自己,我第一次这样做!)非常非常想“在松散模式下或带有大量// @ts-expect-error的情况下,在触摸文件时转换文件 和“任何分散在周围”的东西。 这似乎是摩擦最小、最快、最简单的路径:

  • 该模式通常适用于其他类型的迁移。
  • 感觉更容易处理,因为你可以“随心所欲”地做。
  • 它实际上对于足够小的代码库来说效果很好 - 对于 <1,000LOC 非常好,对于 <10,000LOC 也非常好。[1](https://v5.chriskrycho.com/journal/how-to-do -a-typescript-conversion/#fn1)

因此,这也是我最常推荐给刚开始转换 TypeScript 代码库的人的方法。

很遗憾…

这是一个陷阱!

当您采用更轻松、更直观、更推荐的方法时,您将遇到两个大问题。 在较小的代码库上,这些问题可能并不那么重要,但代码库越大,它们造成的伤害就越大。

首先,您最终将不得不一遍又一遍地传播对各种文件的更改:

  • 每次启用另一个严格性设置时,您都会在许多模块中看到新的类型错误。 其中最大的将是“strictNullChecks”和“noImplicitAny”,但是所有的严格设置都会在没有它们的情况下捕捉到遗漏的东西:毕竟,这就是这些设置存在的原因。 这些通常也不是虚假错误。2因此,您将不得不做另一个 每次启用新的严格性设置时,都会为模块传递“修复类型”。
  • 如果您转换模块但未转换其所依赖的模块,则这些依赖模块中的所有类型都将为“any”。 当您转换这些文件时,您经常会发现使用其 API 的方式存在错误。 就像严格性设置一样,这意味着每次进行此类更改时,您通常最终都必须为其他模块“修复类型”。

我在这里惊恐地引用了“修复类型”,因为它通常是“编写类型并修复错误”。 正如我之前写的

…在许多情况下,复杂性已经存在于代码库中。 TypeScript 转换并没有造成这种复杂性:它暴露了它。 现实世界的 JavaScript 代码通常非常复杂——实际上,聪明——只有当我们尝试用类型来表达代码已经无形地假定的契约时,这种复杂程度才会变得明显。 因此,从 JavaScript 进行转换需要的复杂类型远远多于从一开始就用 TypeScript 编写的代码。 大部分复杂性(永久地!)隐含在 JavaScript 中,而用 TypeScript 编写契约则使其显式化。 这可以实现更好的选择:这个特定的 API 是否真的需要一些复杂的类型,或者我们应该保持简单? 通常:后者。

即便如此,“感觉”就像我们只是一遍又一遍地修复 TypeScript 问题,我认为承认这一点很重要。

这种当你致力于“修复类型”时,ng 在个人层面上可能会让人非常沮丧,因为你发现自己一遍又一遍地使用相同的代码。 与技术水平较低的团队成员进行清晰的沟通也可能很困难,例如 设计师和产品负责人可能会很困惑,为什么我们需要再次花时间为这块代码库做 TypeScript 的事情——“我们不是一个月前就这么做了吗?” 必须解释“是的,我们做到了,但并非一直如此”可能会令人沮丧。

其次,也许更糟糕的是,在采用这种方法时,您“不能”依赖已经转换的东西实际上是安全的。 它们“感觉”比 JS 类型更安全,因为它们在 TS 中……但事实并非如此,因为它们有很多“// @ts-expect-error”和“any”分散在各处。 如果你的“但我们已经转换了这个!”而出现错误,最终可能会让人非常沮丧和沮丧。 模块。 它还破坏了我们在向管理层或合作伙伴证明投资合理性时做出的许多承诺:“我认为 TypeScript 的目的是修复这些类型的错误,那么为什么它不这样做呢?” 就像必须对同一个文件进行多次传递一样,必须回答“嗯,我们将其转换为 TypeScript,但不是一直…”是非常令人不满意的。

最后,这里描述的问题的难度随着代码库的大小呈指数级增长。 对于 1,000 行代码来说,这些问题只是小麻烦。 一万行代码,有点麻烦。 十万行代码,让他们士气低落。 拥有 1,000,000 行代码……您可能永远无法完成。 摩擦力永远不会消失,因此需要不断的努力才能使其保持移动并冲过终点线。

设置“compilerOptions.strict: true”并按顺序遍历依赖关系图的更严格方法意味着您永远不必因为严格性的增加或新类型化的依赖关系而重新访问该文件。 更重要的是,当您以这种方式进行转换时,体验会有很大的好处。 当所有依赖项都已严格类型化时,每次转换新模块时,它都会建立在坚实的基础上。 TypeScript 本身可以通过其代码修复为您完成大量添加类型的工作,因为它可以使用您调用的下游 API 中的信息。 对于你必须自己弄清楚的部分,你仍然需要检查模块的API 使用的所有方式,但你不必检查它使用的所有东西:问题空间被切入 一半。

网络是双赢的:您转换的每个模块实际上都提供类型安全性,并且您接触的每个模块都变得更容易,因为它的基础是安全的。 这个过程就像一个飞轮:你付出的每一点努力都会加速剩下的过程。

这不是免费的午餐。 它通常需要更多的纪律和利益相关者更明确的支持。 您需要每周处理几个模块或类似的事情,腾出一些专门的时间来完成这项工作,而不是“在触摸文件时就进行转换(通常不会完全转换)”。 [3] (https://v5.chriskrycho.com/journal/how-to-do-a-typescript-conversion/#fn3) 但最终,它为每个参与者带来了更好的体验。

注释

  1. 我怀疑这是这种方法如此普遍推荐的重要原因之一! 许多在 TypeScript 社区中具有较高知名度的人在 TypeScript 转换和维护方面拥有更多经验,这些库通常在大约 10,000 行代码范围内,在这种范围内,它可以工作得相当好。 ↩︎
  2. 确实,有时运行时代码是安全的,但会出现类型错误。 不过,随着时间的推移,这种情况在 TypeScript 中越来越少见,根据我的经验,通过启用新的严格性标志而出现的“绝大多数”类型错误表明代码中存在真正的错误。 ↩︎
  3. 您仍然可以逐步改进此方法。 如果您有更大的应用程序/等等。 分成一组较小的包,您可以在包内*执行“在小型库中迭代工作”的方法,同时避免发布类型,直到您获得完全严格的类型。 最终,这两种方法都有一些优点和缺点。 ↩︎
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值