Lyft的TypeScript实践

来自Lyft的前端工程师Mohsen Azimi介绍了Lyft向TypeScript转型的过程,说明JavaScript类型系统的重要性、为什么Lyft选择TypeScript以及他们的一些实践经验。以下内容翻译自作者的博客,查看原文TypeScript at Lyft。

在我刚刚成为JavaScript开发者的时候,当有人说要给JavaScript加入类型系统,我就会问自己:为什么要这么做?

现在,我已经成为一个JavaScript老手,我难以想象没有类型系统支持的JavaScript会是怎样的。大型的JavaScript应用需要类型信息来提升扩展性和可维护性,Lyft的很多JavaScript项目(从Lyft.com网站到我们的内部工具)也不例外。当我还是个JavaScript狂热者的时候,看着Lyft的团队和代码库在膨胀,开始意识到纯粹的JavaScript已经无法支撑起大型的应用了。

但这也并非意味着要扔掉JavaScript。如果能够加入类型系统,一切都会变得不一样。类型系统可以减少bug,开发者也因此能够更加方便地查看代码。下面我将讲述Lyft为什么选择了TypeScript以及是怎么做到的。

Bug!

“Uncaught TypeError: Cannot read property "foo" of undefined”是JavaScript最常见的一个错误。在访问一个未定义(undefined)的引用对象的属性时就会发生这个错误。Lyft的纯JavaScript项目在生产环境经常会出现这个问题。JavaScript的常见错误还包括拼写错误,比如“document.getElementbyId”,这类错误在生产环境会引发大问题。

REST API的类型不匹配问题虽然不常见,但一旦发生就是个大bug。比如,API响应消息里的一个字段从数字类型变成字符串类型,会导致JavaScript出现不可预期的行为。

开发效率

浏览大型的JavaScript代码库不仅耗时而且容易让人感到困惑,找不到函数的定义或搞不清楚函数可以接收哪些参数都是常有的事。以下列这段代码为例:

/**
* Create an input element
* @param {string} type
* @param {string|boolean} value
* @return {HTMLInputElement}
*/
function createInput(type, value) {const el = document.createElement('input');el.type = type;if (type === 'checkbox') {el.checked = value;} else {el.value = value;}return el;
}

这个函数根据传入的类型返回一个HTMLInputElement对象,如果是复选框类型,就把复选框的“checked”属性值设置为传入的值,否则的话就创建一个输入框,并把输入框的值设置为传入的值。

这里可能会出现bug,假设在创建复选框时传入了错误的值,比如:

const input = createInput('checkbox', 'false')

即使代码注释里写得很清楚,仍然无法阻止bug的产生。把字符串“false”赋值给复选框,但复选框的状态仍然会是“true”,因为字符串“false”会被解析成布尔类型的“true”。

而如果有了类型系统,就可以通过重载帮助开发人员写出正确的代码:

/**
* Create an input element
*/
function createInput(type: 'text', value: string): HTMLInputElement;
function createInput(type: 'checkbox', value: boolean): HTMLInputElement;
function createInput(type, value) {// code
}

类型系统让代码变得更强大,通过API级别的语义可以在一开始就把bug扼杀在襁褓里。

类型系统让重构变得更简单,开发人员也因此可以放心地做出代码变更。例如,当一个函数签名发生变化,在调用方代码没有做出相应改动之前是无法通过TypeScript编译的。

强类型的代码之所以更容易进行重构,是因为类型检查器可以确保代码变更可以与项目的其他部分兼容。IDE或代码编辑器为类型系统提供了支持。

带有类型信息的模块更容易维护。使用TypeScript开发的模块在一些编辑器里可以显示出API的提示信息。

TypeScript与FlowType的对决

我们有多种JavaScript类型系统可选择:

  • 带有JSDoc类型注解的Google Closure编译器

  • FlowType

  • TypeScript

虽然我们最终选择了TypeScript,但做出这个决定也并不是那么容易的。我们的团队分成两个阵营,一个倾向于选择FlowType,一个倾向于选择TypeScript。

“FlowType就是JavaScript”

FlowType被认为“就是JavaScript”或者“带有类型注解的JavaScript”。这种看法有失偏颇。FlowType是一门独立的语言,它的语法是JavaScript的超集。它使用了.js作为文件扩展名,所以导致了人们的混淆。实际上,不管是JSX还是FlowType,它们都不是JavaScript。同样,TypeScript和ECMAScript也不是。

调用方类型检查

调用方类型检查是FlowType的一个非常受欢迎的特性。比如:

function power2(a) {return a * a;
}
power2('string')

这个函数接收一个数字类型的参数,如果在调用时传入了一个字符串,FlowType会对此作出警告。而TypeScript则会认为“a”是任意类型,所以可以通过编译。

这个特性看起来令人印象深刻,但我们只要对代码稍作改动,这个特性就不管用了。比如:

function foo(a) {console.log(a.b)
}
foo({})

这段代码可以通过FlowType的编译。

React

因为React和FlowType都是由Facebook开源的,看似FlowType比TypeScript更适合用在React中。但在Lyft的项目中,我们并没有发现把这两者用在React中有什么不同。

流行程度

虽说FlowType和TypeScript看似旗鼓相当,但出于对生态系统未来发展的考虑,我们需要关注它们的流行程度。选择流行程度较高的那一个可以帮助Lyft吸引到更多的开发人才。

要衡量一个开源项目的流行程度并非易事,不过我还是试着努力找出它们之间的对比数据。

StackOverflow上的问题数量:FlowType——900多个;TypeScript——38,000多个。

GitHub上的问题数量:FlowType——1500多个未解决,2200个已关闭;TypeScript——2400多个未解决,11,200个已关闭。

GitHub上的拉取请求:FlowType——60多个未解决,1,200个已关闭;TypeScript——100多个未解决,5000多个已关闭。

npm每月下载数量:FlowType——290多万次;TypeScript——720多万次。

外部类型定义数量:FlowType——340多个,在GitHub上有43000个“流类型”目录,有些库还提供了.flow类型定义;TypeScript——3700多个,在GitHub上有约25万个package.json里包含了类型定义,Facebook的Redux和ImmutableJS也提供了TypeScript类型定义。

我们在内部进行了一个问卷调查,与上述的数据一样,TypeScript在Lyft内部也很受欢迎。

迁移到TypeScript

我们的项目里有大量的纯JavaScript代码,要一下子把它们全部转成TypeScript并非明智的做法。

于是,我们选择了增量迁移。我们使用Webpack编译我们的前端应用,通过TypeScript-loader可以很轻松地将TypeScript引入到Webpack中。有了TypeScript-loader,我们就可以一边使用TypeScript编写新代码,一边零碎地更新旧代码。

TypeScript编译器可以对JavaScript文件进行类型检查,所以我们就利用了这一特性对已有的JavaScript代码进行类型错误检查。当然,这个只对直接被导入到TypeScript中的JavaScript文件有效。不过,最新版本(2.5)的编译器几乎可以直接用于检查独立的JavaScript文件。

推广TypeScript

网上有很多TypeScript的学习资源。TypeScript的官方网站就提供了大量的学习资料,方便开发者入门。另外,TypeScript的语法是JavaScript的超集,所以对于前端开发人员来说非常直观。

lint工具不仅有助于开发者学习TypeScript,对写出一致、流畅的代码也很有帮助。lint工具在没有类型系统的情况下有助于减少有问题的代码,而在有类型系统的情况下就更是能够起到保护代码的作用,所以我们在所有的项目里使用了TSLint。TSLint比一般的lint工具更强大,它可以捕捉到一些很意思的问题,比如awaiting-noon-promise,而这在纯JavaScript的lint工具里是做不到的。

在进行TypeScript培训和往我们的代码库引入TypeScript的过程中,我们发现我们的很多JavaScript代码无法直接使用类型。虽然我们因此感到沮丧,但这也正好说明了我们的代码写得不好,需要进行重构。

Lyft的TypeScript实践

既然引入了TypeScript,Lyft的很多新项目就是纯TypeScript的了。以下是一些使用了TypeScript的项目示例。

TypeScript React转换器

React为它的组件提供了基于运行时的类型系统——PropTypes。我们在Lyft的非TypeScript项目里重度使用了PropTypes。

但在TypeScript里,已经不需要运行时类型检查了,所有的类型检查都发生在编译阶段。于是,我们花了一些时间开发了React JavaScript-to-TypeScript转换器。它利用TypeScript编译器将React组件里的PropTypes转成TypeScript接口。这个工具加速了我们采用TypeScript的进程,为我们节省了大量的时间。

通用异步组件

我们尝试在服务器端动态渲染加载的组件,这个项目完全使用TypeScript开发,它也很好地展示了如何利用类型系统写出可共享的代码。

我们的CSS框架与TypeScript集成

在Lyft,我们使用了一个叫作Tetris的原子CSS库。原子CSS的核心理念是说,重复类名比重复CSS代码的代价更小。原子CSS框架提供了很多小型的CSS类,可以通过组合它们来给元素添加样式。在使用Tetris时,开发人员需要记住大量CSS类名(比如p-a-m,用于给方框增加填充空间)。

为了提升开发效率,我们编写了一个TypeScript文件,它把所有的Tetris类名都导出来,开发人员可以在他们最喜欢的编辑器里导入这个文件,然后就可以使用类名自动完成功能了。

Swagger到TypeScript代码生成

与后端API集成的代码经常会出现bug,这是最让人头痛的问题。后端的变更会影响到前端代码,又或者有时候前端的代码变更会破坏与后端API的兼容性。

我们的后端API是通过OpenAPI(Swagger 2.0)来描述的,我们因此能够规范前端应用的API使用。我们使用Swagger JSON Schema模型为API客户端自动生成TypeScript接口。

原文地址:http://www.infoq.com/cn/news/2017/10/TypeScript-practice-Lyft


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/323192.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Python】Conda的安装

挖个坑,以后自己慢慢填:下载conda后无法使用 conda优势:conda将几乎所有的工具、第三方包都当做package对待,甚至包括python和conda自身!因此,conda打破了包管理与环境管理的约束,能非常方便地…

C++描述杭电OJ 2014. 青年歌手大奖赛_评委会打分 ||

C描述杭电OJ 2014. 青年歌手大奖赛_评委会打分 || Problem Description 青年歌手大奖赛中,评委会给参赛选手打分。选手得分规则为去掉一个最高分和一个最低分,然后计算平均得分,请编程输出某选手的得分。 Input 输入数据有多组&#xff0c…

Java AIO 编程

转载自 java aio 编程 Java NIO (JSR 51)定义了Java new I/O API,提案2000年提出,2002年正式发布。 JDK 1.4起包含了相应的API实现。 JAVA NIO2 (JSR 203)定义了更多的 New I/O APIs, 提案2003提出,直到2011年才发布, 最终在JDK …

P1494-[国家集训队]小Z的袜子【分块优化莫队】

正题 评测记录:https://www.luogu.org/recordnew/lists?uid52918&pidP1494 题目大意 区间任意取两个数,求取到相同的数的概率。 解题思路 假设一个区间有x个y,那么两个都取到y的方案数是x∗(x−1)x*(x-1)x∗(x−1),那么取到相同总共方…

通过Swashbukle给DotNet Core Web API 增加自动文档功能

DotNet Core Web API给开发者提供了一个很好的框架来开发Restful的API。那么这些API接口该如何管理起来呢?Swagger是一个很好的选择,Swagger不需要开发者额外去维护接口文档,只要开发者的接口遵循Restful的规范,Swagger就会根据AP…

【Python】urllib爬取动漫图片

首先附上需要爬取图片的网站&#xff0c;应该算是个冷门网站&#xff0c;够练手用的了&#xff0c;我的博客图片大部分来自于这里 二次元图片网站 筛选src里的数据 用par r’<img src"[^"].jpg">可以筛选出带有里面的内容 htmldasdas <img src"…

P4137-Rmq Problem/mex【莫队,分块】

正题 评测记录:https://www.luogu.org/recordnew/lists?uid52918&pidP4137 题目大意 求区间mex。 解题思路 开始发现aia_iai​很大&#xff0c;开不了桶。但是转念一想&#xff0c;如果ans>n1ans>n1ans>n1仅当前n1个都有&#xff0c;可是最多只有n个&#xff…

【Python】字符串和变量拼接的写法

我的需求是改变url地址的后缀&#xff0c;其他不改&#xff0c;所以直接for循环&#xff0c;变换数字就行 也就是 字符串变量字符串 想着改变后缀就能批量爬图。但是原本的想法是错误的 for num in range(2,8):url"http://www.win4000.com/wallpaper_detail_160877_"…

Java面试,如何在短时间内做突击

转载自 Java面试&#xff0c;如何在短时间内做突击 面试技术文 Java岗 面试考点精讲&#xff08;基础篇01期&#xff09; Java岗 面试考点精讲&#xff08;基础篇02期&#xff09; Java岗 面试考点精讲&#xff08;网络篇03期&#xff09; Java 面试中遇到的坑 Java面试中…

【学校作业】学生数据打印

数据结构课程布置了一门c语言的结构体作业 要求&#xff1a; 五个学生&#xff0c;数据包括学号&#xff0c;姓名&#xff0c;3门课的成绩&#xff0c;从键盘输入5个学生的数据。打印出3门课总平均成绩&#xff0c;以及最高分 强迫症患者表示打印出的数据必须美观&#xff0c; …

P4879-ycz的妹子【分块】

正题 评测记录:https://www.luogu.org/recordnew/lists?uid52918&pidP4879 题目大意 有若干种操作 Cxy:ax−yC\ x\ y:a_x-yC x y:ax​−yIxy:I\ x\ y:I x y:加一个(原本有的话就改变)axya_xyax​yQ:Q:Q:询问所以数的和Dx:D\ x:D x:删除第xxx个(有的才算)数。 解题思路 …

Entity Framework Core 2.0 使用入门

一.前言 Entity Framework&#xff08;后面简称EF&#xff09;作为微软家的ORM&#xff0c;自然而然从.NET Framework延续到了.NET Core。以前我也嫌弃EF太重而不去使用它&#xff0c;但是EF Core&#xff08;Entity Framework Core&#xff09;已经做了很多性能优化&#xff0…

JavaFX中WebView的java与JS代码互相调用

java代码 package main;import javafx.application.Application; import javafx.beans.value.ObservableValue; import javafx.concurrent.Worker; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.layout.VBox; import javafx.scene.w…

【Python】有效资源爬取并集

由于爬虫代码都不多&#xff0c; 所以我决定在这篇博文上更新所有我觉得比较实用的python代码 方便以后自己调用 环境:python3.7 百度图片爬虫 二次元图片爬取 唐三小说爬取 文件格式命名 百度图片爬虫 百度图片网站 import re import requests from urllib import err…

[编程入门]带参数宏定义练习:定义一个带参的宏,使两个参数的值互换,并写出程序,输入两个数作为使用宏时的实参。输出已交换后的两个值。

#include<bits/stdc.h> #define fun(a,b) ta;ab;bt; using namespace std;int main() {int a,b,t;cin>>a>>b;fun(a,b);cout<<a<<" "<<b;return 0; }

Jexus~docker与它产生了暖味

前段时间写了很多docker for .net core的文章&#xff0c;用来快速部署微服务相当给力&#xff0c;而尝到了香头的我们希望把.net frameworks的程序也使用docker来部署一下&#xff0c;那么接下来我就结果一下&#xff0c;在linux,docker上运行和部署.net frameworks应用程序的…

P3870-[TJOI2009]开关【分块】

正题 解题思路:https://www.luogu.org/recordnew/lists?uid52918&pidP3870 题目大意 n个灯&#xff0c;操作[0,l,r][0,l,r][0,l,r]表示l∼rl\sim rl∼r的灯取反&#xff0c;操作[1,l,r][1,l,r][1,l,r]表示询问l∼rl\sim rl∼r之间有多少灯亮着。 解题思路 分块&#xf…

Linux运维常用检查网络工具

一、Ping ping可以检查网络是否连通&#xff0c;可以很好地帮助我们分析和判定网络故障。 ping ip 二、Telnet telnet通常用来查看某个端口是否可访问。 telnet ip port 三、Curl curl是一个利用URL语法在命令行下工作的文件传输工具&#xff0c;1997年首次发行。它支持…

ABP从入门到精通(1):aspnet-zero-core项目启动及各项目源码说明

一.ABP的简单介绍 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称。 ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应用程序的新起点&#xff0c;它旨在成为一个通用的WEB应用程序框架和项目模板。 ASP.NET Boilerplate 基于DDD的经典分层架构思想…

[编程入门]宏定义的练习:输入两个整数,求他们相除的余数。用带参的宏来实现,编程序。

#include<bits/stdc.h> #define N a%b; using namespace std;int main() {int a,b;cin>>a>>b;cout<<N;return 0; }