1. 首页 > 手游资讯攻略网

为啥子TypeScript 脑壳一半边疼是为啥子

作者:admin 更新时间:2024-10-20
摘要:TypeScript 在 2017 年到 2019 年期间发展得很快,有很多值得关注的地方。在 2018 年的 JavaScript 状态调查中,几乎一半的受访,为啥子TypeScript 脑壳一半边疼是为啥子

 

TypeScript 在2017 年到2019 年间发展迅速,有很多值得喜欢的地方。在2018 年JavaScript 现状调查中,近一半的受访者表示他们已经尝试过TypeScript 并将再次使用它。那么,你应该用它来开发大型项目吗?

TypeScript 的增长

TypeScript 是增长最快的语言之一,也是目前编译为JavaScript 的领先语言。

Google 趋势:2014 年至2019 年TypeScript 的增长

截至2018 年9 月,GitHub 贡献者数量增长最快的语言

虽然这些非常令人印象深刻,但它们还不足以主宰整个JavaScript 生态系统。

Google 搜索趋势:2014 年至2018 年JavaScript(红色)与TypeScript(蓝色)

2008年至2018年各种编程语言的GitHub代码库:TypeScript未进入前5名。

看来TypeScript 在2018 年达到了拐点,2019 年将会有大量项目采用它。作为JavaScript 开发者,你可能别无选择。你无法控制TypeScript 如何发展,你所能做的就是学习和使用它。

但是,在考虑是否使用它时,您应该对其好处和成本有一个现实的了解。它会产生积极还是消极的影响?

根据我的经验,两者都会有,但也缺乏积极的投资回报率。很多开发人员都喜欢它,包括我自己,但所有这些都不是没有代价的。

背景

我以前使用静态类型语言比较多,包括C/C++和Java。一开始我很难适应JavaScript 的动态类型,但一旦习惯了,就好像看到了一条又长又黑的隧道的出口。静态类型有很多值得喜欢的地方,动态类型也是如此。

过去几年我一直在使用TypeScript,并且每天都在练习它一年多了。我领导过几个使用TypeScript 作为主要编程语言的团队,我看到了TypeScript 给项目带来的影响,并将其与类似的大型原生JavaScript 项目进行了比较。

2018年,去中心化应用开始爆发,其中大部分使用智能合约和开源软件。对于有价值的互联网应用程序来说,错误会让用户付出代价。编写可靠的代码比以往任何时候都更加重要,因为这些项目通常是开源的。我认为我们使用TypeScript 做出了正确的决定,其他TypeScript 团队可以更轻松地与我们集成,同时保持与JavaScript 项目的兼容性。

我对TypeScript 的优点、成本和缺点有了更深入的了解。我想说的是,它并没有我希望的那么成功。我不会在另一个大型项目中使用TypeScript,除非它得到显着改进。

我喜欢 TypeScript 的哪些方面

从长远来看,我对TypeScript 保持乐观。 TypeScript 还有很多我喜欢的地方。我希望TypeScript 开发者和支持者能够将我的观点视为建设性的批评,而不是敌对的咆哮。

静态类型有助于记录功能、阐明用法并减少认知开销。例如,我发现Haskell 的类型很有用并且使用起来很便宜,但有时Haskell 的高级类型系统会妨碍。在Haskell(或TypeScript)中使用转换器类型并不容易,而且可能比非类型语言更糟糕。

我喜欢TypeScript 的一点是,TypeScript 中的注释是可选的,使用结构化类型,并且在一定程度上支持类型推断(尽管类型推断还有很大的改进空间)。

TypeScript 支持接口,一个接口可以有多个实现。接口是TypeScript 最重要的功能之一,我希望这个功能也可以内置到JavaScript 中。

从数字看 TypeScript 的投资回报率

我将使用-10 到10 的分数范围从几个维度对TypeScript 进行评分,以便让您更好地了解TypeScript 是否适合大型应用程序。

大于0 分表示正面影响,小于0 表示负面影响,3 分至5 分表示影响较大,得分为2 表示影响中等,得分为1 表示影响较小。

这些数字很难精确,而且有些主观,但我已经估计了实际项目的实际成本和回报。

在小样本项目中,客观数据噪声太大,无法做出具有可靠误差范围的客观判断。在一个项目中,原生JavaScript 的错误密度比TypeScript 低41%。但在另一个项目中,TypeScript 的bug 密度比原生JavaScript 版本低4%。

由于误差幅度太大,我放弃了客观量化,转而关注交付速度和所花费的时间。

由于涉及主观性,必须容忍一定范围的误差(如图所示),但总体投资回报率具有参考价值。

TypeScript 成本和收益分析:可能的负投资回报率

我已经听到一些大惊小怪的人对如此之少的积极分数提出异议,我并不完全不同意他们的观点。 TypeScript 确实提供了一些非常有用且强大的功能,这是毫无疑问的。

要理解为什么正面得分如此之少,首先要了解我的比较方法:我不只是将TypeScript 与JavaScript 进行比较,我还在比较用于开发原生JavaScript 的工具。

让我们深入探讨下面的每个比较点。

开发者工具:我最喜欢的TypeScript 功能是它可以减轻开发者的认知负担。它提供接口类型提示,可以在编程时实时捕获潜在的错误。这也是TypeScript给我们带来的最大的实际好处。益处。如果不是因为某些插件为原生JavaScript 带来了类似的功能,我会给TypeScript 更高的分数。

大多数TypeScript 拥护者似乎不太了解TypeScript 的竞争对手是什么。在选择开发工具时,不是在TypeScript 和原生JavaScript 之间进行选择,而是在TypeScript 和整个JavaScript 开发工具生态系统之间进行选择。当您使用自动完成、类型推断和lint 工具时,原生JavaScript 自动完成和错误检测可以达到TypeScript 的80% 到90%。在进行类型推断并使用ES6 默认参数时,您将获得类型提示,就像使用带有类型注释的TypeScript 代码一样。

具有类型推断的本机JavaScript 自动完成示例

公平地说,如果使用默认参数提供类型提示,则不需要为TypeScript 代码提供注释,这减少了类型语法开销(这是使用TypeScript 的开销之一)。

TypeScript 在这方面可以说更好,但它并不能弥补成本。

API 文档:TypeScript 的另一个优点是它提供了更好的API 文档,并且始终与源代码同步。您甚至可以直接从TypeScript 代码生成API 文档。但在JavaScript 中,使用JSDoc 和Tern.js 可以达到相同的效果。我个人并不是JSDoc 的忠实粉丝,因此TypeScript 在这里获得了更高的分数。

重构。在大多数情况下,如果您从重构TypeScript 项目中获得了显着的好处,则说明您的代码耦合过于紧密。如果TypeScript 为您节省了很多重构的痛苦,那么紧耦合可能仍然会给您带来许多其他可以避免的问题。

另一方面,一些公司拥有一个由许多相互关联的项目组成的生态系统,这些项目共享相同的代码库(例如谷歌著名的monorepo)。使用TypeScript 可以帮助他们更改API 设计。进行API 更改的开发人员需要确保他们的更改不会破坏依赖这些库的其他项目。 TypeScript 为这个非常有限的TypeScript 用户子集节省了大量时间。

我说非常有限的子集,因为封闭的大型生态系统代码库是例外,不受此规则的约束。当对库API 进行重大更改时,如果该API 被更广泛的生态系统使用,那么这些更改很可能会破坏您甚至不知道存在的其他代码。

在更加去中心化的传统图书馆生态系统中,人们避免对API 进行重大更改,而是根据开放-封闭原则添加新功能(API 对扩展开放,对重大更改关闭)。这基本上就是网络平台的演变方式,但也有一些例外。这就是为什么React 自React 0.14 以来仍然支持某些功能,尽管它们已被其他更好的选项取代。 React 不断发展并添加新功能,从根本上改善开发人员体验,而不会破坏旧功能。例如,即使改进的React Hooks API 已经成熟,React 仍然支持类组件。

这使得对整个生态系统的改变成为可选的而不是必需的。团队可以根据需要逐步升级其软件,而不是让图书馆团队在整个生态系统中进行代码更改。

即使在需要更改整个生态系统代码的情况下,类型推断和react-codemod也可以为我们提供帮助(不需要—— TypeScript)。

我最初给重构一个心理零,然后把它从列表中删除,因为我真的很喜欢开放封闭原则、推理和代码模型。然而,一些团队在某些情况下从重构中获得了真正的好处。

类型安全似乎也没有多大区别。 TypeScript 支持者经常谈论类型安全的好处,但几乎没有证据表明类型安全对错误密度有显着影响。这很重要,因为代码审查和TDD 会产生巨大的差异(仅TDD 的差异就占40% 到80%)。将TDD 与设计评审、规范评审和代码评审相结合,可以将bug 密度降低90% 以上。其中一些进程(尤其是TDD)除了TypeScript 可以捕获的错误之外,还可以捕获许多TypeScript 无法捕获的错误。

伦敦大学学院的Cheng Gau 和Earl T. Barr 以及微软研究院的Christian Bird 在一份研究报告中表示,TypeScript 理论上最多只能解决20% 的“未解决的bug”。公共错误是在实施阶段之后仍然存在并提交到公共代码库的错误。

他们认为自己低估了TypeScript 的影响,因为他们假设所有其他质量措施都已经应用,但没有判断其他错误预防措施的质量。他们承认这个变量存在,只是根本不考虑它。

根据我的经验,绝大多数团队已经应用了一些指标,但很少有人能够很好地应用所有重要的错误预防措施。在我的团队中,我们使用设计审查、规范审查、TDD、代码审查、lint 和模式验证,这些都对错误密度产生重大影响,并且可以将类型错误减少到几乎为零。

根据我的经验,除了linting 之外,其他因素对代码质量的影响比静态类型更大。

如果您没有正确应用这些错误预防措施,我很确定您可以使用TypeScript 减少15% 到18% 的错误,但会错过其他80% 的错误,直到它们投入生产并开始引起问题。出现。

有些人会认为TypeScript 提供实时错误反馈,因此您可以更早地捕获错误,但问题是类型推断、lint 和TDD 也可以工作(我编写了一个监视脚本,可以在保存文件时运行该单元)测试这样我也可以获得直接反馈)。您也可能会说这些方法是有代价的。但由于TypeScript 总是会错过80% 的错误,因此无论如何都无法安全地跳过它们,因此它们的成本适用于ROI 的两端,并且已经考虑在内。

这项研究仅着眼于预先已知的错误,包括为修复问题而更改的代码行,即在引入类型之前已知的问题和潜在的解决方案。但即使提前知道bug 的存在,TypeScript 也无法检测到其他85% 的公共bug,—— 只能捕获15%。

为什么TypeScript 检测不到这么多bug,为什么我将bug 密度减少20% 称为“理论最大值”?首先,GitHub 上78% 的公开错误是由规范错误引起的。在很大程度上,未能正确实现规范是最常见的错误类型,这导致TypeScript 无法检测或阻止绝大多数错误。在上述研究中,研究人员对一系列“TypeScript 无法检测到的错误”进行了分类。

TypeScript 未检测到的错误的直方图

上面的“StringError”是指正确类型的字符串包含不正确的值(例如不正确的URL)。 BranchError 和PredError 是指导致错误代码路径的逻辑错误。还有许多其他TypeScript 无法处理的错误。 TypeScript 几乎不可能检测到超过20% 的bug。

但20%似乎很多了!那么为什么TypeScript 在错误预防方面没有获得更高的分数呢?

因为有太多静态类型无法检测到的错误,所以忽视其他质量控制措施(例如设计审查、规范审查、代码审查和TDD)是不负责任的。因此,认为TypeScript 是唯一可以用来防止错误的工具是不公平的。

忽略其他措施是不安全的:规范错误:80%,类型错误:20%

假设您的项目包含1000 个错误,而没有采取错误预防措施。在应用其他质量措施后,潜在错误的数量减少到了100 个。现在我们可以看到TypeScript 可以防止多少错误。近80% 的bug 无法通过TypeScript 检测到,而所有TypeScript 能检测到的bug 都可以通过TDD 等其他方法检测到。

不采取行动:1000 个错误;

经过额外的操作:仍然捕获了100 个bug——900;

添加TypeScript 后:仍然存在80 个错误,并捕获了——20 个错误。

有人认为如果有静态类型就没有必要写那么多测试用例。只能说,这些人的想法毫无根据。即使你使用TypeScript,你仍然需要额外的措施。

在审查和TDD 之后添加TypeScript 只能捕获全部错误的一小部分

评论和TDD 在没有TypeScript 的情况下捕获了1000 个bug 中的900 个。如果你忽略评论和TDD,TypeScript 会捕获1000 个bug 中的100 个。显然,你不一定要在它们之间进行选择,而是在采取其他步骤后添加TypeScript,此时获得的改进非常有限。

在耗资数百万美元的大型开发项目中实施了质量控制系统后,我可以告诉您,我对高成本系统实施的有效性的期望是错误减少30% 到80%。您可以通过以下任一方法获得此类好处:

设计和规范审查(减少高达80% 的错误); TDD(将剩余错误再减少40% 到80%);代码审查(一小时的代码审查可节省33 小时的维护时间)。类型错误只是所有可能错误的一小部分,还有其他方法可以捕获类型错误。结果非常清楚地告诉我们:TypeScript 在减少bug 方面并没有给我们带来太多好处。在最好的情况下,只能实现非常有限的减少率,并且您仍然需要应用其他质量措施。

事实似乎与TypeScript 的炒作不太相符。但这些并不是唯一的好处,不是吗?

新的JavaScript 功能和JavaScript 编译,Babel 都为原生JavaScript 做了这两项工作。

差不多就这样了。我不了解你,但我感到有点失望。如果我们可以使用其他工具来获取类型提示、自动完成并减少原生JavaScript 的错误,那么剩下的问题是:TypeScript 给我们带来的好处值得我们投资吗?

为了弄清楚这一点,我们需要仔细研究TypeScript 的成本。

招聘:在《JavaScript 现状报告》调查中,近一半的受访者曾经使用过TypeScript 并表示会再次使用,另外33.7% 的人想学习,5.4% 的人曾经使用过TypeScript,但不会再使用,13.7% 的人表示会再次使用TypeScript。人们对学习TypeScript 不感兴趣。这意味着招聘目标减少了近20%,这对于需要大量招聘TypeScript 开发人员的团队来说可能是一笔巨大的成本。招聘是一个相当昂贵的过程,可能会拖延数月,并占用其他开发人员(需要担任面试官)的时间。

另一方面,如果你只需要雇用一两个TypeScript 开发人员,那么你的职位可能对几乎一半的候选人更具吸引力。对于小型项目来说这可能没问题,但对于数百或数千人的团队来说,这会对投资回报率产生负面影响。

初始培训:因为这些是一次性成本,所以相对较低。已经熟悉JavaScript 的团队将在2 到3 个月内有效地使用TypeScript,并在6 到8 个月后轻松使用它。这肯定会比招聘的成本更高,但如果这是一次性成本,那肯定是值得的。

缺少功能——HOF、组合、对更高类型泛型的支持等:TypeScript 和JavaScript 并不完全共存。这是我在使用TypeScript 时面临的最大挑战之一,因为熟练的JavaScript 开发人员经常会遇到类型难以使用的情况,他们会花几个小时在谷歌上搜索示例,试图找出如何解决此类问题。

持续辅导:虽然人们很快就能学会如何有效地使用TypeScript,但需要很长时间才能获得充分的信心。我仍然觉得还有很多东西需要学习。在TypeScript 中输入相同内容的方法有很多种,弄清楚每种方法的优缺点、整理最佳实践等可能比最初的学习曲线花费更长的时间。

例如,新的TypeScript 开发人员倾向于使用注释和内联类型,而经验丰富的TypeScript 开发人员已经学会重用接口并创建单独的类型,以减少内联注释的令人困惑的语法。更有经验的开发人员还会找到强制类型在编译时更有效地捕获错误的方法。

类型开销:类型开销的成本包括添加类型、测试、调试和维护类型注释所花费的所有时间。调试类型是经常被忽视的成本。类型注释也有自己的错误。类型可能太严格、太宽松或错误。

您可能还会注意到语法噪音的增加。在像Haskell 这样的语言中,类型通常是函数定义上方列出的一小行代码。在TypeScript 中,尤其是泛型函数,它们通常是侵入性的,并且默认情况下是内联定义的。

为什么语法噪音会出现问题?您希望保持代码整洁,就像您希望保持房屋整洁一样:

更多混乱=更多隐藏错误的地方=更多错误。混乱使您更难找到所需的信息。就像调谐收音机频道一样,消除噪音以更好地听到正确的信号,减少语法噪音就像将收音机调谐到适当的频道一样。

语法噪音是TypeScript 的一大成本,可以通过两种方式改进:

使用更高级别的类型来更好地支持泛型可以消除一些模板语法噪音。 (参见Haskell 的类型系统)。

默认情况下,鼓励使用单独的类型而不是内联类型。如果避免内联类型可以成为最佳实践,那么类型语法将与函数实现隔离,这将使类型签名和实现更易于阅读,因为它们不会相互竞争。

结论

TypeScript 有很多地方我仍然喜欢,我希望它能改进。其中一些成本问题将来可能会通过添加新功能和改进文档来解决。

但是,我们不应该忽视这些问题,开发者在不考虑成本的情况下夸大TypeScript 的好处是不负责任的。

TypeScript 可以而且应该在类型推断、高阶函数和泛型方面做得更好。 TypeScript 团队还有一个巨大的机会来改进文档,包括教程、视频、最佳实践,这将帮助TypeScript 开发人员节省大量时间并显着降低使用TypeScript 的成本。

随着TypeScript 的不断发展,我希望更多的用户能够度过蜜月期并认识到它的成本和局限性。随着用户数量的增加,越来越多的人才将专注于提供解决方案。

我仍然在小型开源库中使用TypeScript,主要是为了方便其他TypeScript 用户。但我不会在下一个大型应用程序中使用当前版本的TypeScript,因为项目越大,使用TypeScript 的复合成本就越高。

英文原文: