7个拒绝使用TypeScript的借口
译者按: TypeScript 学习成本不高,项目切换成本不低,不过还是值得试一试的!
自从 6 年前诞生,TypeScript 逐渐被各大型公司接受。 也许你有充足的理由说服自己不要使用它,这些都让你错失了 TypeScript。在这篇文章中,我会列举大家为何不选用 TypeScript 的一些原因,比如学习曲线、工具、开发效率、稳定性以及对标准的兼容性。
1. 学习曲线过于陡峭
首先需要强调一点:TypeScript 并不是一个完全崭新的开发语言。像 CoffeeScript 和 Reason 吸收了 Ruby 和 OCaml 的语法和语义融入到 JavaScript 语言中。TypeScript 从某种程度上说,更加保守。
它只是在 JavaScript 的基础上添加了类型系统(TypeScript 是 JavaScript 的超集),所以它的学习曲线会相对平滑。特别是相对于完全切换到不同编程语言来说。
下面是一段 TypeScript 代码:
class Greeter { greeting: string; constructor(message: string) { this.greeting = message; } greet() { return "Hello, " + this.greeting; }}
对比用 ES6 编写的相同功能的代码:
class Greeter { constructor(message) { this.greeting = message; } greet() { return "Hello, " + this.greeting; }}
正如 TypeScript 语言的一个设计者 Anders Hejlsberg 说到:“如果你懂得 JavaScript,那么你就已经懂得 TypeScript 了。” 稍后,我会介绍如何将现有的项目切换到 TypeScript 去。
2. JavaScript 是标准,TypeScript 不是
当 TypeScript 在 2012 年诞生的时候就有了类和模块的概念,标准 JavaScript 直到 2015 年都还没有!
今天,TypeScript 语言紧随 ECMAScript 的定义,几乎实现了Stage 3所有的提案。也就是说,当你在使用 TypeScript 的时候,你其实已经在用最新的 JavaScript。而且感谢 ES3/ES5 编译器,最终输出的.js
文件即使在旧版本的浏览器也不会出问题。
3. 破坏了 JavaScript 的动态性
如果你熟练使用脚本语言工作,你会喜欢它惊人的开发效率。你可以在代码中随时修改数据结构,而不必事先声明。这种自由度,也伴随着代价。这种动态语言有了 bug 有时候很难搞定,它不会像静态语言一样,在编译的时候有做类型的验证来保证代码的正确性。
比如下面的 JavaScript 代码:
function greeter(person) { return "Hello, " + person.firstName + " " + person.lastName;}
通过代码我们可以判断 person 参数是一个对象,它有 firstName 和 lastName 两个属性。但是我们无法保证在运行时一定是这样。
特别是当你的项目越大,和类型相关的 bug 就越可能触发。
为了避免出现问题,我们可以加上很多运行时的验证,以及单元测试:
function greeter(person) { if (!person || !person.firstName || !person.lastName) { throw new Error("invalid arguments"); } return "Hello, " + person.firstName + " " + person.lastName;}// Jasmine spec:describe("greeter", function() { it("returns greeting on valid input", function() { expect(greeter({ firstName: "James", lastName: "Hetfield" })).toEqual( "Hello, James Hetfield" ); }); it("throws on invalid input", function() { expect(() => greeter()).toThrowError("invalid arguments"); expect(() => greeter(5)).toThrowError("invalid arguments"); expect(() => greeter({ firstName: "Jason" })).toThrowError( "invalid arguments" ); });});
但是,这样的实现方法很累赘,开发者需要在每个函数前面都加上这样的判断来保证正确性。换个思路,如果我们给函数的参数加上类型,这个事情不就省去了么。
interface Person { firstName: string; lastName: string;}function greeter(person: Person) { return "Hello, " + person.firstName + " " + person.lastName;}// Jasmine spec:describe("greeter", function() { it("returns greeting", function() { expect(greeter({ firstName: "James", lastName: "Hetfield" })).toEqual( "Hello, James Hetfield" ); });});
上面的代码更加简洁,甚至连测试也只需要考虑代码的逻辑而不是数据的正确性。
TypeScript 有强大的类型系统来做类型推断。你并不需要像在 C# 或则 Java 中那样,显示的列出数据的类型。下面是一个例子:
let user = { firstName: "James", lastName: "Hetfield" };console.log(greeter(user));
上面的代码可以成功编译。请注意我们并没有声明 user 是 Person 类型的。
TypeScript 编译器并不会强制你必须在所有地方都声明类型,你可以在正确性和效率上自己做一个权衡。你甚至可以在项目不同的区域自定义类型的严格程度。这样的灵活性超出了以前所有的开发语言。
4. TypeScript 火不过 5 年
没有人可以确保哪种语言、工具或者框架一定可以持续活跃,特别是在前端这一领域。一个StackOverflow 博客的作者这样写道:
在 JavaScript 框架的使用有两个明显的阶段。因为框架的流行首先有一个快速的上升期,然后当开发者接触到其它新的技术后,慢慢的平稳的下降。整个周期也就几年的时间。
你本身处于一个快速迭代的行业,而你开发的项目可以从某些特定的技术中受益。尽管 1、2 年后,你已经切换到其它技术,但是你当时学到的经验依然值得。
5. TypeScript 没有社区驱动
TypeScript 由微软在 2012 年发布,考虑到公司的形象以及其开发平台,很容易产生“只不过是给.Net 开发者打造的一个 JavaScript 开发玩具而已”的想法。特别是当时只有 Visual Studio 对 TypeScript 有足够的支持。事实上,TypeScript 的源代码最开始是发布在CodePlex上,一个微软自己的 GitHub。
不过,现已不复当年,TypeScript 身后的开发团队意识到为了让语言被广泛接受,他们需要深入前端开发社区,为开发者提供高质量的开发工具并且接受反馈不断改进。他们不仅仅是把代码公开然后默默开发,而是拥抱了一个开放式开发的方式。
在 2014 年,TypeScript 的源代码移到了GitHub托管。而且整个开发都在 GitHub 上公开透明。他们同时接受外部提交贡献,包括记录 bug,提出建议以及提交 pull request。所有的 issue 会定期更新,几天之内就会有回应。核心团队发布了语言的设计理念用来指导团队不偏离目标并积极吸收社区的建议。他们有一个保持更新的roadmap(基本上每两个月发布一次更新),并记录重大的更新。
在我写这篇文章的时候,所有主流的跨平台 IDE 或则编辑器像 Eclipse、Webstorm、Emacs、Vim、Sublime、Atom 和 VS Code 都对 TypeScript 有着很好的支持,要么是内置的,要么通过插件。为了和现有的库和框架交互,核心团队花了很多时间来创建类型定义文件。另一个值得一提的是编译器 API 的文档非常完善,可以很好地构建第三方工具。TypeScript 的开发者团队也鼓励开发者在StackOverflow上提出技术问题。
6. 切换成本过高
为了充分发挥 TypeScript 的优势,你需要花费不少时间来声明类型并修复编译错误,这会很繁琐。TypeScript 的创建者们有考虑到原有 JavaScript 项目切换的问题,提供了相应的方案。
根据官方的迁移指导,你可以直接运行 TypeScript 的编译器。编译器会抛出一些低层级的 bug,比如没有 return 语句,代码块中有永远不会执行的代码。然后,你可以将.js 文件重命名为.ts 文件,然后再处理编译结果。编译器默认的选项没有特别严格,你可以开启更加严格的验证。整个迁移有一个底线:整个过程是相当平滑的,它不会使开发中断。你可以选择慢慢将一个项目切换到 TypeScript,或则来个快速的切换(参考:3 天搞定 60 万行代码的迁移)。社区还有一个叫做TypeWiz的项目可以自动给代码添加类型信息。
7. 但是我使用的库都是基于 JavaScript
为了和现有的 JavaScript 模块无缝对接,TypeScript 支持类型声明文件。举个例子,如果你使用的库中导出了一个 camelize 的函数,你可以定义一个类型声明文件并给出如下的定义:
declare function camelize(s: string): string;
当你导入类型声明文件后,TypeScript 会正确获取函数对应的类型。
TypeScript 同时也发布了DefinitelyTyped,这个 repository 包含了所有流行的库的类型定义文件。在我写这篇文章的时间点,DefinitelyTyped 已经包含了超过 5000 个 JavaScript 包的类型定义。使用方法非常简单:
npm install --save-dev @types/jasmine
类型文件会自动被编译器包含,并且在编辑器中也会有对应的代码提示。几乎所有或则大多数你使用的包都已经有对应的类型定义文件了,要么自身有相应的类型定义文件,要么在 DefinitelyTyped 可以找到,你只需要下载安装就好。Slack 的开发团队分享了他们的经验:
考虑到代码维护,我们真的很感谢 TypeScript 强大的生态系统。我们是 React 和 Node/npm 的重度用户,第三方库也有对应的类型文件极大方便了开发。很多我们导入的库已经和 TypeScript 兼容。如果没有,那么也可以在 DefinitelyTyped 找到。比如,React 并没有自带类型定义,你可以通过npm install @types/react
来安装,无需其他额外操作。
结论
使用静态类型检车可以帮助你消除很多 bug,对于一个基于 Node/JavaScript 的项目,TypeScript 是你最好的选择。对于一个相对小或则试验性的项目,也许没有必要。但是,一个大型的项目,TypeScript 绝对值得你尝试。它带来的好处远远大于它带来的复杂度。一些大型的公司都开始使用也是最好的证明,比如 Google、Slack、Asana、Ember 等等。希望这篇文章给你启发,让你重新燃起对 TypeScript 尝试的欲望。