别骗自己了!你不用 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 "Hello, " + this.greeting; } }
这是 JavaScript(ES6)的相同代码:
复制代码
class Greeter { constructor(message) { this.greeting = message; } greet() { return "Hello, " + 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 "Hello, " + person.firstName + " " + person.lastName; }
通过查看代码,我们可以推断 person 参数应该是具有 firstName 和 lastName 属性的 Object。但在运行时我们不能保证一定会是这样的。
项目越大,与类型相关的错误发生的几率就越高。
为了避免出现这类错误,可以使用运行时类型检查和单元测试,如下所示:
复制代码
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 通过采用结构化类型系统来体现 JavaScript 的动态特性,并且在类型推断方面做得非常出色,这意味着你不必像 C# 或 Java 那样明确表达类型。以下是使用我们使用 TypeScript 编写的函数示例:
复制代码
let user = { firstName: "James", lastName: "Hetfield" }; 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