[译] Angular: Why TypeScript?

作者:Victor Savkin
原文地址:https://vsavkin.com/writing-a...

关于作者

Victor Savkin 是 nrwl.io 的其中一位创始人,向企业团队提供Angular的咨询服务。他以前在谷歌的Angular核心团队,并建立了依赖注入、变更检测、表单和路由器模块。

nrwl.io 是由Angular核心贡献者创建,以帮助团队成功实现充满展望项目的咨询服务。

正文

Angular使用TypeScript编写,在这篇文章里,我将告诉你,我们为什么做了这样的选择。我还将分享我使用TypeScript的经验:它如何影响我编写和重构代码的方式。

TypeScript 里优秀的工具

TypeScript最大的卖点就是它的工具。它提供先进的自动完成,导航和重构工具,而以上工具,几乎完全满足了大型项目的需求。没有他们的话,对于修改代码的恐惧会将代码置于半只读的状态,并且使大规模的重构非常危险和昂贵。

TypeScript不是唯一可以编译为JavaScript的类型语言,也有其他更强类型系统的语言,并且在理论上也可以提供绝对优秀的工具。但实际上,它们中的大多数除了编译器之外没有其他任何东西。

这是因为想要构建出丰富的开发工具,必须从开发的第一天起就明确这个目标,这一点,TypeScript团队早已准备好了。

这就是为什么他们构建的语言服务可以被编辑用来提供类型检查和自动完成。如果你想知道为什么有那么多出色的编辑器具有TypeScript的支持,那么答案就是语言服务。

可靠的智能感知和基本重构(例如:重命名一个符号),对编写,尤其是重构代码的过程产生了巨大的影响。尽管这很难衡量,但我觉得以前可能需要几天的重构现在可以在一天之内完成。

虽然TypeScript极大地提高了代码编辑的经验,但它使开发人员的设置更加复杂,尤其在与在页面上删除ES5脚本相比时。此外,你不能使用分析JavaScript源代码的工具:(如JSHint),但通常有足够的替代品。

TypeScript 是 JavaScript 的超集

因为TypeScript是JavaScript的超集,所以你不需要通过一个大的重写来迁移到它。你可以慢慢地做,一次一个模块。

选择一个模块,重命名.js文件到.ts,然后增量地添加类型注释。当你完成这个模块时,选择下一个模块。一旦输入了整个代码库,就可以开始调整编译器设置,使其更加严格。

这个过程可能需要一些时间,但对于Angular迁移到TypeScript时不是一个大问题,它逐渐让我们在过渡期间不断开发新的功能和修复bug。

TypeScript 使得抽象清晰可见

一个好的设计就是定义良好的接口。而且,用支持它们的语言来表达接口的想法要容易得多。

例如,假设有一个图书销售应用程序,可以通过一个注册用户通过UI或通过某种API通过外部系统进行购买。

正如您所看到的,这两个类都扮演着购买者的角色。尽管对于应用程序非常重要,但购买者的概念在代码中并没有明确表达出来。没有名为购买者.js的文件。因此,修改代码的人可能会忽略这个角色的存在。

仅仅通过查看代码来判断哪些对象可以扮演购买者的角色,以及该角色的方法是困难的。
我们不确定,也不会从我们的工具中得到很多帮助。我们必须手动推断这些信息,这是缓慢且容易出错的。

现在,将它与我们明确定义了 Purchaser 接口 的版本进行比较。

类型化的版本清楚地说明了我们有 Purchaser 接口,而User和ExternalSystem类实现了它。所以TypeScript接口允许我们定义抽象/协议/角色。

重要的是要认识到TypeScript并没有强迫我们引入额外的抽象。Purchaser 抽象在代码的JavaScript版本中,但是没有明确定义。
在静态类型语言中,子系统之间的边界是使用接口定义的。由于JavaScript缺乏接口,所以在普通JavaScript中边界不能很好地表达。由于不能清楚地看到边界,开发人员开始依赖于具体类型而不是抽象接口,从而导致紧密耦合。

TypeScript 使代码更容易阅读和理解

是的,我知道这看起来并不直观。让我用一个例子来说明我的意思。让我们来看看这个函数jQuery.ajax()。我们能从它的签名中得到什么信息?

我们唯一能确定的是这个函数有两个参数。我们可以猜测这些类型。也许第一个是字符串,第二个是配置对象。但这只是猜测,我们可能错了。我们不知道什么选项进入设置对象(它们的名称和类型),或者该函数返回什么。

在不检查源代码或文档的情况下,我们不可能调用这个函数。检查源代码并不是一个好的选择——拥有函数和类的目的,是在不知道如何实现它们的情况下使用它们。换句话说,我们应该依赖于他们的接口,而不是他们的实现。我们可以检查文档,但这并不是最好的开发经验——它需要额外的时间,而且文档经常过期。

因此,尽管很容易阅读jQuery.ajax(url,settings),真正理解如何调用这个函数,我们需要阅读它的实现或它的文档。

以下是一个类型版本:

它给了我们更多的信息。

  • 这个函数的第一个参数是一个字符串。

  • 设置参数是可选的。我们可以看到所有可以传递到函数中的选项,不仅是它们的名称,还包括它们的类型。

  • 函数返回一个JQueryXHR对象,我们可以看到它的属性和函数。

类型化签名肯定比未类型化的签名长,但是:string,:JQueryAjaxSettings和JQueryXHR并不是混乱的。
它们是提高代码的可理解性的重要文档。我们可以更深入地理解代码,而不必深入到实现或读取文档中。
我的个人经验是,我可以更快地阅读类型化代码,因为类型提供了更多的上下文来理解代码。
但如果读者能找到一项关于类型如何影响代码可读性的研究,请留下评论。

与编译到JavaScript的许多其他语言相比,TypeScript的不同之处在于它的类型注释是可选的,而 jQuery.ajax(url,settings)仍然是有效的TypeScript。因此,TypeScript的类型不是一个开关,而是一个表盘。
如果您发现代码没有类型注释,这对于阅读和理解来说是微不足道的,那么请不要使用它们。仅当它们添加值时才使用类型。

TypeScript 限制表达吗?

动态类型的语言有较落后的工具,但它们更具可塑性和表现力。我认为使用TypeScript可以使您的代码更加严格,但是比人们认为的要宽松得多。
让我告诉你我的意思,假设我使用ImmutableJS来定义Person记录。

我们如何类型record?让我们从定义一个名为Person的接口开始:

如果我们尝试以下代码

TypeScript编译器会指出,他不知道PersonRecord,但是其实PersonRecord是由Person反射创建的,二者是相容的。
一些有FP背景的人可能会说:“是否只有TypeScript有从属类型!”但事实并非如此。TypeScript的类型系统并不是最先进的。

但它的目标是不同的。并不是为了证明这个程序是100%正确的。它是关于给你更多信息并启用更大的工具。因此,当类型系统不够灵活时,可以使用快捷方式。因此,我们可以将创建的记录转换为如下:

它之所以有效是因为类型系统是结构化的。只要创建的对象具有正确的字段——名称和年龄。

你需要接受这样一种心态:在TypeScript走捷径是可以的。
只有这样,你才会发现使用语言是令人愉快的。

例如,不要尝试将类型添加到某些funky元编程代码中——很可能您无法静态地表达它。
类型化代码周围的所有内容,并告诉typechecker忽略funky位。在这种情况下,您将不会失去大量的表现力,而且您的大部分代码将保持工具和可分析性。

这类似于试图获得100%的单元测试代码覆盖率。而获得95%通常不是那么困难,获得100%是具有挑战性的,并且可能会对应用程序的架构产生负面影响。

可选的类型系统还保留了JavaScript开发工作流。您的应用程序的大部分代码库可以“断开”,但您仍然可以运行它。TypeScript将继续生成JavaScript,甚至当类型检查器发出警告或报错时。这在开发过程中非常有用。

为什么选择TypeScript

现在有很多可以选择的选项:ES5、ES6(Babel)、打字稿、Dart、PureScript、Elm等。那么,我们为什么选择了TypeScript?

让我们先从ES5开始比较。
ES5比TypeScript有一个明显的优势:它不需要一个转流机。这允许您保持构建设置的简单性。您不需要设置文件观察者、transpile代码、生成源映射。它只是工作。

ES6需要一个转换设备,因此构建设置与TypeScript没有太大区别。但它是一个标准,这意味着每一个编辑器和构建工具要么支持ES6,要么支持它。这是一个较弱的论据,在这一点上,有优秀的TypeScript支持,它能被用于大多数编辑者。

Elm和PureScript是优雅的语言,具有强大的类型系统,可以证明您的程序比TypeScript多得多。在Elm和PureScript中编写的代码比在ES5中编写的代码要多得多。

每个选项都有优点和缺点,但我认为TypeScript在一个很好的地方,使它成为大多数项目的最佳选择。
TypeScript占了良好的静态语言的95%的有用性,并将其引入到JavaScript生态系统中。您仍然觉得自己编写ES6:您一直使用相同的标准库、相同的第三方库、相同的习语和许多相同的工具(例如:Chrome开发工具)。它会给你很多东西,但不会强迫你离开JavaScript生态系统。

[译] Angular: 我们为什么选择TypeScript?的更多相关文章

  1. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  2. HTML5数字输入仅接受整数的实现代码

    这篇文章主要介绍了HTML5数字输入仅接受整数的实现代码,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  3. amaze ui 的使用详细教程

    这篇文章主要介绍了amaze ui 的使用详细教程,本文通过多种方法给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  4. html5简介_动力节点Java学院整理

    这篇文章主要介绍了html5简介,用于指定构建网页的元素,这些元素中的大多数都用于描述网页内容,有兴趣的可以了解一下

  5. ios – 如何使用Objective C类中的多个参数调用Swift函数?

    本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

  6. ios – Swift 4添加手势:覆盖vs @objc

    我想在我的视图中添加一个手势,如下所示:但是,在Swift4中,我的编译器给出了以下错误:建议添加@objc以将此实例方法公开给Objective-C.实现此目的的另一个选项将覆盖touchesBegan()函数并使用它来处理点击.我试图以“Swift”的方式做到这一点,而不必带入Obj-C.有没有纯粹的Swift方式来添加这个轻击手势而不使用@objc?

  7. ios – 将视频分享到Facebook

    我正在编写一个简单的测试应用程序,用于将视频从iOS上传到Facebook.由于FacebookSDK的所有文档都在Objective-C中,因此我发现很难在线找到有关如何使用Swift执行此操作的示例/教程.到目前为止我有这个在我的UI上放置一个共享按钮,但它看起来已禁用,从我读到的这是因为没有内容设置,但我看不出这是怎么可能的.我的getVideoURL()函数返回一个NSURL,它肯定包含视

  8. ios – 以编程方式在Swift中添加联系人

    我想在Swift中以编程方式添加联系人.我发现了一些Objective-C示例,但我没有让它们工作,甚至在Objective-C中也没有.我不希望这涉及到AddressBookUI,因为我想从我自己的UI中获取值.解决方法这是在Swift中添加联系人的快速方法.我在我的iPhone5iOS7.1上验证了它,因为我发现模拟器并不总是与我的手机对AB的东西相同.您可以添加一个按钮并指向此方法:顺便说一下–它假设你已经分配了一个地址簿var,你可以通过覆盖viewDidAppear来打开视图.它也会执行安全提示

  9. ios – 为目标c中的方法传递未知类型的参数,可能吗?

    是否可以将未知类型的参数传递给objective-C方法?在C#中你可以写实现这一点,但我知道Objective-C没有泛型,所以有没有其他方法可以在Objective-C中实现这一点?我需要这个,因为我想创建一个方法来改变不同对象的文本颜色,如UITextField和UIButton的占位符文本.所以我的计划是创建一个名为textWhite的方法,然后在此方法中检查对象的类型,然后运行匹配的代码以使文本颜色变为白色.解决方法是的,可以传递未知类型的参数.见下面的例子.请参考使用id对象的链接作为参数Us

  10. ios – Swift指针算术和解除引用;将一些类似C的地图代码转换为Swift

    我有一点似乎没有工作的Swift代码……解决方法您正在指定locationPointer指向新位置,但仍在下一行中使用ptr,并且ptr的值尚未更改.将您的最后一行更改为:或者你可以改变指向var的指针并推进它:

随机推荐

  1. Angular2 innerHtml删除样式

    我正在使用innerHtml并在我的cms中设置html,响应似乎没问题,如果我这样打印:{{poi.content}}它给了我正确的内容:``但是当我使用[innerHtml]=“poi.content”时,它会给我这个html:当我使用[innerHtml]时,有谁知道为什么它会剥离我的样式Angular2清理动态添加的HTML,样式,……

  2. 为Angular根组件/模块指定@Input()参数

    我有3个根组件,由根AppModule引导.你如何为其中一个组件指定@input()参数?也不由AppModalComponent获取:它是未定义的.据我所知,你不能将@input()传递给bootstraped组件.但您可以使用其他方法来做到这一点–将值作为属性传递.index.html:app.component.ts:

  3. angular-ui-bootstrap – 如何为angular ui-bootstrap tabs指令指定href参数

    我正在使用角度ui-bootstrap库,但我不知道如何为每个选项卡指定自定义href.在角度ui-bootstrap文档中,指定了一个可选参数select(),但我不知道如何使用它来自定义每个选项卡的链接另一种重新定义问题的方法是如何使用带有角度ui-bootstrap选项卡的路由我希望现在还不算太晚,但我今天遇到了同样的问题.你可以通过以下方式实现:1)在控制器中定义选项卡href:2)声明一个函数来改变控制器中的散列:3)使用以下标记:我不确定这是否是最好的方法,我很乐意听取别人的意见.

  4. 离子框架 – 标签内部的ng-click不起作用

    >为什么标签标签内的按钮不起作用?>但是标签外的按钮(登陆)工作正常,为什么?>请帮我解决这个问题.我需要在点击时做出回复按钮workingdemo解决方案就是不要为物品使用标签.而只是使用divHTML

  5. Angular 2:将值传递给路由数据解析

    我正在尝试编写一个DataResolver服务,允许Angular2路由器在初始化组件之前预加载数据.解析器需要调用不同的API端点来获取适合于正在加载的路由的数据.我正在构建一个通用解析器,而不是为我的许多组件中的每个组件设置一个解析器.因此,我想在路由定义中传递指向正确端点的自定义输入.例如,考虑以下路线:app.routes.ts在第一个实例中,解析器需要调用/path/to/resourc

  6. angularjs – 解释ngModel管道,解析器,格式化程序,viewChangeListeners和$watchers的顺序

    换句话说:如果在模型更新之前触发了“ng-change”,我可以理解,但是我很难理解在更新模型之后以及在完成填充更改之前触发函数绑定属性.如果您读到这里:祝贺并感谢您的耐心等待!

  7. 角度5模板形式检测形式有效性状态的变化

    为了拥有一个可以监听其包含的表单的有效性状态的变化的组件并执行某些组件的方法,是reactiveforms的方法吗?

  8. Angular 2 CSV文件下载

    我在springboot应用程序中有我的后端,从那里我返回一个.csv文件WheniamhittingtheURLinbrowsercsvfileisgettingdownloaded.现在我试图从我的角度2应用程序中点击此URL,代码是这样的:零件:服务:我正在下载文件,但它像ActuallyitshouldbeBook.csv请指导我缺少的东西.有一种解决方法,但您需要创建一个页面上的元

  9. angularjs – Angular UI-Grid:过滤后如何获取总项数

    提前致谢:)你应该避免使用jQuery并与API进行交互.首先需要在网格创建事件中保存对API的引用.您应该已经知道总行数.您可以使用以下命令获取可见/已过滤行数:要么您可以使用以下命令获取所选行的数量:

  10. angularjs – 迁移gulp进程以包含typescript

    或者我应该使用tsc作为我的主要构建工具,让它解决依赖关系,创建映射文件并制作捆绑包?

返回
顶部