T

TypeScript 的问题

2025-06-07

TypeScript 的问题

大家好,我叫 Ryan,这是我在 dev.to 上的第一篇文章。我经常在 medium.com 上写文章,但我想试试 dev.to。我非常热衷于响应式库和前端 JavaScript 性能。我是Solid.js的作者,Solid.js 是JS 框架基准测试中性能最好的库之一。但今天我想写点别的。

我使用 TypeScript 已经大约一年了。时间几乎不够对它下结论,但几个月来我一直想写这篇文章。每次都拖延,希望最终能有所收获。我还意识到,作为一个库的编写者,我可能并非最优秀的评判者,我感觉自己就像被推入了深渊。所以我想给我信任的、拥有不同经验水平和编程背景的人一个公平的机会。因此,我不仅将所有开源库都转换为 TypeScript,而且在 6 个月后,我还询问了我所在初创公司的开发人员,他们是否愿意使用 TypeScript 重写我们的核心应用程序。他们对学习 TypeScript 的兴趣各不相同,但都持开放态度。现在又过去了几个月,我终于觉得可以发表一些看法了。那就让我们开始吧。

TypeScript 是一门艺术,而不是一门科学

我编程已经大约25年了。这些年来,我用过几十种类型语言。但TypeScript是第一个尝试将类型置于动态语言之上的语言。这本身似乎是一个了不起的壮举。不过,动态类型语言几十年前就做到了。一度,摆脱类型甚至被视为一种进步。

从简单的例子开始,一切看起来都很熟悉。你添加了一些注解,然后惊讶地发现它竟然不允许你将字符串赋值给数字。你确保函数拥有清晰的参数和返回类型,然后你开始感觉自己明白了。然后你遇到了需要传入不同对象的地方。你首先想到的是放宽定义,但随后你看到一个泛型的例子,意识到 TypeScript 对泛型的使用比你习惯的 C++ 或 Java 要自由得多。更酷的是,它们的类型通常可以推断出来,这意味着你甚至不需要注解,一切都会神奇地运行。

直到你添加几个额外的层级,你开始遇到不一致的地方,或者无法推断类型的地方。前几天,我正在帮助我的首席开发人员处理一些工厂函数的类型问题,这个函数会生成一些钩子,这些钩子会返回 JS 生成的类中的 CSS,这些类是传入工厂的样式定义和传入钩子的 props 的结果。他遇到了一些非常基础的问题,但不太明白为什么他的类型不起作用。于是我开始使用泛型来分配多个值,并为项目类型创建包装器以返回值。有人告诉我,经过几次尝试,我基本上已经成功了。我承认我感觉很好,但开发人员看起来很困惑。你看,他以为自己终于掌握了 TypeScript,完全不知道我刚才做了什么。所以我花了接下来的半个小时来解释。最后,他明白了,但他仍然没有好转,因为他从未这样想过。说实话,几个月前我也遇到过同样的情况。

你听说过编程是艺术吗?开发人员会选择如何建模他们的问题,并拥有自己的风格。每个人的代码都不一样。我记得当我还是一个年轻的开发人员时,我会努力找到解决问题的最巧妙的方法,并为此感到非常自豪,直到一位资深开发人员破口大骂,问我为什么不做最简单的事情。随着时间的推移,我的代码变得更有针对性,也更简洁。由于 JavaScript 潜力巨大,TypeScript 拥有如此多的工具来执行看似相似的事情,以至于你很容易采取一种无法完全解决问题的策略。除非你亲身体验,否则很难知道什么是正确的方法。但是,由于 JavaScript 不可能安全地将所有东西都输入类型,你甚至不知道你尝试做的事情是否可行,或者你只是对这个问题的想法是错误的。

这导致了一种非常奇怪的情况:问题越复杂,即使你去寻求帮助,传达代码的意图与功能也同样重要。在谈论可能的解决方案时,这就好比人们在看现代艺术时试图批判钉在墙上的卫生纸卷的意图和情感。你可以花费数小时来完善一个优雅的类型解决方案,而无需提交任何新的可行代码。当你做对了的时候,你会感到非常高兴和聪明。这是最高级别的元编程。当你尝试使用第三方库时,情况会变得更加尴尬,因为第三方库更关心花费几个月的时间来使其正确,而不是得到一些可以工作的东西(与此同时,当前的类型实际上已被破坏)。

正如我之前提到的,编程本身就具备这些特点,但如果你的工具也具备这些特点,那就太奇怪了。这种程度的不确定性,这种需要用工具完全站在你正在解决的编程问题一边来解决难题的心态,我认为开发人员喜欢这种感觉,因为他们天生就是问题解决者。但当涉及到效率和生产力等因素时,这种感觉就显得多余了。每次使用 TypeScript,我都会想起自己还是个年轻、缺乏经验的程序员,做了很多不必要的事情。

TypeScript 专注于 Ceremony

我经常想,那些对 TypeScript 赞不绝口的人,究竟有多少真正用过 JavaScript。我几乎只用了五年 CoffeeScript,最近几年才回归到 ES6。我不会建议大家现在就转用 CoffeeScript,除非只是想简单体验一下它的一些特性。CoffeeScript 在某些方面与 TypeScript 截然相反,它体现了 JavaScript 的其他特性。忘掉类型吧。大多数情况下,你甚至不需要声明变量。如果你读过这些人谈论 JavaScript 的方式,我就能想象他们会如何看待 CoffeeScript。

CoffeeScript 比 JavaScript 更能提高我们团队的工作效率,这会让你感到惊讶吗?那是一个不同的时代,我不确定它现在是否会有这么大的进步。但让我来描述一下。编写 CoffeeScript 很像编写伪代码。所以在你计划好如何处理编程任务之后,你往往会直接把东西写出来。需要一个新的变量,就开始使用它。产生一个想法非常快。语法简洁很好,因为在 JavaScript 中 80 行的代码在 CoffeeScript 中只有 30 行左右。当然,你会运行它,发现它不能正常工作,因为你错过了一个空检查。然后你会添加一个?(可选链运算符)。然后你意识到你的逻辑在第二个循环中是错误的。所以你需要重构。

我使用 TypeScript 时看到的情况是,30 行 CoffeeScript 文件现在变成了 150 行。如果不滚动,我无法在 IDE 窗口中看到整个文件。在 CoffeeScript 开发人员开始重构的同时,TypeScript 开发人员刚刚协调了所有类型,并即将首次运行他们的代码。类型注释不会花费太多时间,除非您需要查找您不知道的类型(说真的,对于浏览器来说,MDN 在这里真是个救星),但这里的趋势是确保所有内容都匹配,以便在第一次运行时一切正常。当然,TypeScript 开发人员从来没有遇到过浏览器出错的情况,Cannot read 'name' of undefined但当他们在第二个循环中意识到他们的逻辑是错误的时候,我们的第一个开发人员已经在测试重构了。

许多 JavaScript 开发人员已经习惯了这种开发方式,只是把东西扔到墙上,看看它是否能坚持下去。他们快速测试想法,而代码并不完美。这在编译语言中并不是一种奢侈。如果你要等几分钟,最好在构建之前确保代码能够正常工作。对我来说,这与瀑布式方法和敏捷方法之间的区别没什么不同。我们知道,一些大公司在采用敏捷方法后仍然会遇到一些问题,我觉得 TypeScript 的情况也类似。别误会我的意思。CoffeeScript 可能产生了更多 bug,但尝试一些东西通常可以更快地揭示你的假设是否错误。少浪费时间去完善你本来就不会用的东西。

TypeScript 很嘈杂

因为它具有更高的噪声信号比。您正在查看的代码中功能性部分会减少。我已经讨论过需要更多代码,但这超出了初始开发的范围。我知道这可能更多地基于观点,但是当 Dan Abramov(React 核心团队)最近在 Twitter 上说,当他查看别人的代码时,类型实际上会妨碍他查看代码时,我意识到我并不孤单。当您只是想查看流程时,类型信息可能会成为噪音。说实话,与上一个相比,这个问题不那么严重,因为它不会改变您处理编码的方式。但这还是有点问题的。我们可以很容易地过滤掉注释,但仅仅是将函数声明从一行增加到五行,就会让你走上一条总是查看更少内容的道路。

TypeScript 是 JavaScript 的一个子集

我对这一点印象非常深刻。从功能支持的角度来看,它严格来说是一个超集。然而,人们使用它是为了进行编译时类型检查,所以一旦这成为你的必需品,你就无法用 TypeScript 实现某些功能了。我在编写 Solid.js 时就遇到了这个问题。它使用 JSX 的方式与 React 完全不同,它有很多函数式模式,比如柯里化,以及支持路径和动态参数的函数。更不用说它的核心性能调优得非常好,所以我不愿意改变底层 JavaScript 的编译结果。说实话,在三周内,我遇到了十几个未解决的 TypeScript 问题,并且我自己也提交了两个。我从 TypeScript 社区获得了许多帮助,并且对从事和支持它的人们没有任何恶意。但是,对于可解决的解决方案,最好的选择是:更改 API 或添加另一个函数调用以使编译器按照你想要的方式工作,我当然非常不配合。

最终,我决定不在$JSX 属性中使用带前缀的自定义绑定,而是使用 JSX 命名空间,并引入带有特殊字符的固有元素(所有这些都是 JSX 规范支持的)。为了避免路径问题,我违背自己的判断引入了另一种语法。我认为,理解这一点至关重要:JavaScript 中有很多模式无法实现类型安全,而更多模式则需要专家来判断是否类型安全。

显然,作为一名底层库的编写者,我很快就遇到了这些问题,但我甚至看到这些问题影响了应用程序开发人员。他们不得不改变处理互操作层的方式,因为它对 TypeScript 不太友好。同样,在使用第三方库时,也会遇到一些 TypeScript 独有的怪异特性。假装你还没有把灵魂卖给 TypeScript,读读这篇 Material UI 指南吧。我为什么要注册这个?

结论

如果您将 TypeScript 视为一种独立的语言,尽管其功能集比 JavaScript 要小,但您还是可以做得很好。如果您将 TypeScript 视为具有类型的 JavaScript,您会感到失望。问题是,尽管我或我周围的人有过多么糟糕的经历,但我们仍在坚持使用它。作为一名库作者,这很有意义,因为有很多人想要它。到目前为止,这并不意味着我不愿意做出任何妥协,所以我致力于支持它。我知道在我脑海里的某个地方,这样做会限制我的创造力。我的一些更有趣的想法不适用于 TypeScript,所以采取这种立场可能会损害我研究它们的动力。但今天的Solid.js已经非常令人印象深刻。

在我的团队里,情况很分裂。后端开发人员使用 TypeScript 并不费力,而且随着他们找到了更好的项目结构方法,他们的解决方案也随着知识的积累而扩展。然而,在前端,情况几乎是一场灾难。TypeScript 基本上决定了其他技术的选择。我们一直在犹豫是使用 TypeScript 还是使用这个库。到目前为止,我们选择 TypeScript 是因为它带来的潜力。事后看来,我本来不会把它引入到前端,但我觉得我们已经开始克服困难,所以投入的时间是值得的。讽刺的是,我认为很多宣传的好处实际上有害。

TypeScript 并没有提高生产力或可读性。它并没有特别改进现代 JavaScript 的功能集。如果说有什么改进的话,那就是它限制了你的功能。但它并非全是坏事。它促使开发人员编写代码文档。它在处理第三方 API 时设置了约定。然而,我认为最大的好处是它让开发人员更安心。它激发了开发人员的信心,这是我们所有人都应该支持的,即使这门语言本身可能是我 25 年编程生涯中见过的最糟糕的妥协之作。

TypeScript 可能不是我们需要的语言,但它是我们现在应得的语言。

文章来源:https://dev.to/ryansolid/the-trouble-with-typescript-4fpp
PREV
如何在 Linux Mint 21.3 上设置 Microsoft Office:综合指南
NEXT
React Hooks 发布回顾:两年后 最初的反应 蜜月期 第一年 两年后 那么发生了什么? 我们接下来该去哪里?