自六年前诞生以来,TypeScript已经走过了漫长的道路,并被一些领先的Web公司采用。你可以不使用TypeScript,但你不使用TypeScript的理由有可能欠缺考虑。
在这篇文章中,我将介绍大家比较关心的与TypeScript相关的一些问题,比如学习曲线、工具、开发速度、可维护性和标准合规性问题。
1. 学习曲线过于陡峭
需要注意的是,TypeScript并非一门全新的语言。CoffeeScript和Reason分别将JavaScript装扮成其他编程语言的语法和语义——Ruby和OCaml,而TypeScript的目标则更为保守一些。
它只是在JavaScript之上添加了一个类型系统(TypeScript是JavaScript的超集)。所以TypeScript的学习曲线比较平缓,从JavaScript切换到TypeScript并不像从一门语言切换到另一门语言那么繁琐。
这是TypeScript的一个代码片段:
class Greeter { greeting: string; constructor(message: string) { this.greeting = message; } greet() { return \u0026quot;Hello, \u0026quot; + this.greeting; }}
这是JavaScript(ES6)的相同代码:
class Greeter { constructor(message) { this.greeting = message; } greet() { return \u0026quot;Hello, \u0026quot; + this.greeting; }}
正如TypeScript的共同作者Anders Hejlsberg说的那样,“如果你了解JavaScript,那么差不多也就了解了TypeScript”。
2. JS是标准的,而TypeScript不是
当TypeScript于2012年问世时,带来了类和模块等功能,直到2015年ECMAScript第6版(ES6或ES2015)最终确定时才成为“标准JavaScript”。TypeScript模块的原始实现偏离了ES6,但在标准最终确定的同一年,它做了更新(保持向后兼容性),以便与ES6规范保持一致。
现在,TypeScript严格遵循ECMAScript规范,实现了大多数达到阶段3的提议。也就是说,当你在编写TypeScript时,实际上是在编写符合标准的现代JavaScript。经过ES3/ES5转换器的转换,输出的.js文件在旧版浏览器中也可以正常运行。
3. 它破坏了JS的动态特性
如果你已经使用过脚本语言,那么就应该体验到它们所带来的开发速度上的提升。你可以随时自由处理数据结构,而无需事先声明它们。然而,这种自由需要付出代价。动态类型的程序比静态类型的程序更难以维护,因为流经程序的数据没有编译时验证。
来看看这个简单的JavaScript示例:
function greeter(person) { return \u0026quot;Hello, \u0026quot; + person.firstName + \u0026quot; \u0026quot; + person.lastName;}
通过查看代码,我们可以推断person参数应该是具有firstName和lastName属性的Object。但在运行时我们不能保证一定会是这样的。
项目越大,与类型相关的错误发生的几率就越高。
为了避免出现这类错误,可以使用运行时类型检查和单元测试,如下所示:
function greeter(person) { if (!person || !person.firstName || !person.lastName) { throw new Error('invalid arguments'); } return \u0026quot;Hello, \u0026quot; + person.firstName + \u0026quot; \u0026quot; + person.lastName;}// Jasmine spec:describe(\u0026quot;greeter\u0026quot;, function() { it(\u0026quot;returns greeting on valid input\u0026quot;, function() { expect( greeter({firstName: 'James', lastName: 'Hetfield'}) ).toEqual('Hello, James Hetfield'); }); it(\u0026quot;throws on invalid input\u0026quot;, function() { expect(() =\u0026gt; greeter()).toThrowError('invalid arguments'); expect(() =\u0026gt; greeter(5)).toThrowError('invalid arguments'); expect(() =\u0026gt; greeter({firstName: 'Jason'})).toThrowError('invalid arguments'); });});
但这样做有点累赘,而且开发人员需要负责验证数据。我们是否可以为函数添加一个简单的类型注解就可以搞定一切?
interface Person { firstName: string; lastName: string;}function greeter(person: Person) { return \u0026quot;Hello, \u0026quot; + person.firstName + \u0026quot; \u0026quot; + person.lastName;}// Jasmine spec:describe(\u0026quot;greeter\u0026quot;, function() { it(\u0026quot;returns greeting\u0026quot;, function() { expect( greeter({firstName: 'James', lastName: 'Hetfield'}) ).toEqual('Hello, James Hetfield'); });});
上面的例子更加简洁一些,让我们可以专注于验证测试中的业务逻辑。
TypeScript通过采用结构化类型系统来体现JavaScript的动态特性,并且在类型推断方面做得非常出色,这意味着你不必像C#或Java那样明确表达类型。以下是使用我们使用TypeScript编写的函数示例:
let user = { firstName: \u0026quot;James\u0026quot;, lastName: \u0026quot;Hetfield\u0026quot; };console.log(greeter(user));
这段代码可以成功编译。请注意,我们并没有明确地说明user变量实现了Person接口,也没有明确定义扩展它的类。TypeScript的设计目标之一不是为了创建一个“正确的类型系统”,而是“在正确性和生产力之间取得平衡”。
TypeScript编译器不会强制你声明类型,类型安全的程度由你自己来决定。你甚至可以决定在项目的不同区域应用不同级别的类型安全严格程度。这种灵活性不是传统的静态类型语言可以提供的。
4. 它活不过5年
没有人能够知道5年后会出现什么样的语言、工具或框架,尤其是在Web领域。以下是StackOverflow博客作者撰写的有关JavaScript框架生命周期的内容:
JavaScript框架似乎有两个主要的阶段。随着框架在开发人员中变得越来越流行,先是有一个快速的上升期,然后随着开发人员转向新的开发技术,开始出现稳定下降。整个过程只会持续几年时间。
我们都处在一个快速发展的行业,如果你和你的项目今天能够从某些技术中受益,那么就可以考虑使用它们。即使你在1至2年内会使用其他技术,你在这一两年时间内获得的好处也会加速你的项目开发,所以是值得的。
5. 它不是由社区驱动的
微软于2012年发布了TypeScript。考虑到微软公司本身及其开发平台的声誉,人们很容易认为TypeScript只是“适合.NET开发人员口味的JavaScript”。在当时,Visual Studio是唯一支持该语言的IDE,而这似乎进一步强化了这种看法。或者是因为该项目的源代码最初发布在CodePlex上,CodePlex是微软运行的另一个类似GitHub的代码托管站点。
但从那以后,很多事情都发生了变化。TypeScript团队意识到,如果他们希望这门语言得到更广泛的采用,他们需要通过提供高质量的工具和接受用户反馈来更好地融入Web开发社区。他们不只是在真空中开发TypeScript,而是完全接受了开放式开发的概念。
2014年,TypeScript的代码被迁移到GitHub,并在GitHub上进行开发。他们邀请开源社区贡献者加入,贡献者可以记录错误、记录提案和提交拉取请求。问题跟踪器中的问题会定期得到解决,每个提交通常会在几天内通过评审。核心团队已经发布了TypeScript的设计目标,这有助于项目在接受社区意见的同时坚持自己的使命。他们保持更新路线图(现在大约每两个月发布一次新版本),并将重大变更记录下来。
在撰写本文时,所有主要的跨平台IDE和文本编辑器(如Eclipse、Webstorm、Emacs、Vim、Sublime、Atom、VS Code)都内置或通过插件成功支持TypeScript。核心团队还投入了大量精力来创建类型定义文件,以便与使用纯JavaScript编写的现有库和框架实现互操作。另一件值得注意的事情是TypeScript提供了一组编译器API,可用于创建很多有价值的第三方语言工具。
考虑到所有这些因素以及TypeScript严重依赖ECMAScript的事实,我认为,这个项目其实是由社区驱动的,而且已经有好几年时间了。以下是EmberJS联合创始人Tom Dale在2017年对TypeScript的评论:
最重要的是,TypeScript团队的专业精神令人印象深刻。在目前怎样的生态系统中,TypeScript的持续迭代改进令人耳目一新。
6. 转换现有项目的工作量太大
为了在项目中充分利用TypeScript,你将不得不花时间声明类型,并修复编译器错误,这可能有点枯燥乏味。但是,TypeScript作者在构建这门语言时也考虑到了JavaScript生态系统,并为在现有项目中采用TypeScript提供了平滑的过度。
根据官方迁移指南,只需通过TypeScript编译器运行现有的JavaScript代码,就可以获得TypeScript带来的好处。编译器将捕获一些低级错误,例如函数末尾缺失return语句或者无法触及的代码块。然后,你可以逐个将.js文件重命名为.ts,并解决编译器捕获到的问题。默认的编译器选项非常宽松,当然你也可以启用更严格的模式。你甚至可以在项目的不同部分使用不同的编译器选项集,让你的团队可以按照自己的进度采用TypeScript。最重要的是,从JavaScript到TypeScript有一条平稳的过渡路径,不会阻碍整个公司的开发进度。
你可以选择逐步将现有项目转换为TypeScript,或进行“大爆炸”式的转换,或者只是在新项目中使用TypeScript。还有像TypeWiz这样的社区项目可以自动将缺少的类型信息添加到代码中。
7. 我使用的库都是用JavaScript编写的
为了实现与现有JavaScript代码的互操作性,TypeScript还提供了TypeScript声明文件。例如,如果你使用的JavaScript库导出了一个叫作camelize的全局函数,那么它的TypeScript声明将如下所示:
declare function camelize(s: string): string;
将这个类声明导入项目后,TypeScript就会知道这个函数的类型。
在发布TypeScript的同一年,出现了另外一个社区存储库DefinitelyTyped——用于流行库的声明文件。在撰写本文时,它包含超过5,000个JavaScript包的声明。使用这些声明非常简单,例如,如果你在项目中使用了Jasmine测试框架,只需运行:
npm install --save-dev @types/jasmine
然后编译器会自动包含这些类型,你将在代码编辑器中获得动态类型检查和自动完成功能。你使用的所有或大多数软件包应该已经为它们创建了高质量的类型声明。Slack的工程团队分享了他们的经验:
考虑到代码可维护性,我们要感谢TypeScript的生态系统。作为React和Node/npm生态系统的重度用户,第三方库的类型定义可用性是一个巨大的优势。我们导入的很多库都已兼容TypeScript。如果定义不随模块本身一起提供,可能可以在神奇的DefinitelyTyped项目中找到。例如,React不附带类型定义,但可以通过npm install @types/react来安装,无需进一步配置。
结论
使用静态类型检查可以帮助你消除掉很多错误,TypeScript是Node/JavaScript项目最流行的静态类型解决方案。在一个相对较小或实验性的项目中,使用TypeScript可能并不值得,但对于大型项目,其好处远大于额外的开销,这个已经得到谷歌、Slack、Asana、Ember等公司的证实。希望本文为你提供了一个关于TypeScript的全新视角,也许是时候再给它一次机会了。
英文原文:
https://blog.logrocket.com/7-bad-excuses-for-not-using-typescript-dbf5e603a9a8
更多内容,可关注前端之巅(ID:frontshow)