TypeScript 真的值得吗?

做者:Paul Cowanjavascript

翻译:疯狂的技术宅前端

原文:blog.logrocket.com/is-typescri…java

未经容许严禁转载ios

img

在开始以前,但愿你们知道,我是 TypeScript 爱好者。它是我在前端 React 项目和基于后端 Node 工做时的主要编程语言。但我确实有一些疑惑,因此想在本文中进行讨论。迄今为止,我已经用 TypeScript 写了至少三年的代码,因此 TypeScript 作得的确不错,并且知足了个人需求。git

TypeScript 克服了一些很难解决的问题,并成为前端编程领域的主流。 TypeScript 在这篇列出了最受欢迎的编程语言的文章中排名第七位。github

不管是否使用 TypeScript,任何规模的开发团队都应该遵循如下惯例:typescript

  • 编写良好的单元测试——应在合理范围内涵盖尽量多的生产代码
  • 结对编程——额外的审视能够捕捉到的错误远远超过语法错误
  • 良好的同行评审流程——正确的同行评审能够检查出许多机器没法捕获的错误
  • 使用 linter,例如 eslint

TypeScript 能够在这些基础之上增长额外的安全性,但我认为这在编程语言需求列表中应该排在后面。编程

TypeScript 不是健全的类型系统

我认为这多是 TypeScript 当前版本的主要问题,可是首先让我定义 健全非健全 的类型系统。后端

健全性

健全的类型系统是可以确保你的程序不会进入无效状态的系统。例如,若是表达式中的静态类型为 string,则在运行时,要保证在评估它时仅得到 stringapi

在健全的类型系统中,绝对不会在编译时或运行时产生表达式与预期类型不匹配的状况。

固然 TypeScript 有必定程度的健全性,并捕获如下类型错误:

// 'string' 类型不可分配给 'number' 类型
const increment = (i: number): number => { return i + "1"; }

// Argument of type '"98765432"' is not assignable to parameter of type .
// 没法将参数类型 '"98765432"' 分配给参数类型'number'。
const countdown: number = increment("98765432");
复制代码

不健全

100% 的健全性不是 Typescript 的目标,这是在 non-goals of TypeScript 列表中第 3 条中明确指出的事实:

...适用健全或“证实正确的”类型的系统。相反,要在正确性和生产率之间取得平衡。

这意味着不能保证变量在运行时具备定义的类型。我能够用下面的例子来讲明这一点:

interface A {
    x: number;
}

let a: A = {x: 3}
let b: {x: number | string} = a; 
b.x = "unsound";
let x: number = a.x; // 不健全的

a.x.toFixed(0); // 什么鬼?
复制代码

上面的代码是 不健全 的,由于从接口 A 中可以知道 a.x 是一个数字。不幸的是,通过一系列从新分配后,它最终以字符串形式出现,而且如下代码可以编译经过,可是会在运行时出错。

不幸的是,这里显示的表达式能够正确编译:

a.x.toFixed(0);
复制代码

我认为这多是 TypeScript 最大的问题,由于健全性不是目标。我仍然会遇到许多运行时错误,tsc 编译器不会标记这些错误。经过这种方法,TypeScript 在健全和不健全的阵营中脚踏两只船。这种半途而废的现象是经过 any 类型强制执行的,我将在后面提到。

我仍然须要编写不少的测试,这让我感到沮丧。当我第一次开始使用 TypeScript 时错误地得出结论:能够没必要编写这么多单元测试了。

TypeScript 挑战了现状,并声称下降使用类型的认知开销比类型健全性更重要。

我可以理解为何 TypesScript 会走这条路,而且有一个论点指出,若是健全类型系统可以获得 100% 的保证,那么对 TypeScript 的使用率讲不会那么高。这种观点随着 dart 语言的逐渐流行( Flutter 现已被普遍使用)被反驳了。健全性是 dart 语言的目标,这里是相关的讨论(dart.dev/guides/lang…

不健全以及 TypeScript 暴露在严格类型以外的各类转义符使它的有效性大大下降,不过这总比没有强一些。个人愿望是,随着 TypeScript 的流行,可以有更多的编译器选项可供使用,从而使高级用户能够获得 100% 的可靠性。

TypeScript 不保证运行时的类型检查

运行时类型检查不是 TypeScript 的目标,所以这种愿望可能永远不会实现。例如在处理从 API 调用返回的 JSON 时,运行时类型检查将是有好处的。若是能够在类型级别上进行控制,则不须要那么多的错误种类和单元测试。

正是由于没法在运行时保证全部的事情,因此可能会发生:

const getFullName = async (): string => {
  const person: AxiosResponse = await api();
  
  //response.name.fullName 可能会在运行时返回 undefined
  return response.name.fullName
}
复制代码

尽管有一些很棒的支持库,例如 io-ts,但这可能意味着你必须复制本身的model。

可怕的 any 类型和严格性选项

any 类型就是这样,编译器容许任何操做或赋值。

TypeScript 在一些小细节上每每很好用,可是人们倾向于在 any 类型上花费不少时间。我最近在一个 Angular 项目中工做,看到不少这样的代码:

export class Person {
 public _id: any;
 public name: any;
 public icon: any;
复制代码

TypeScript 让你忘记类型系统。

你能够用 any 强制转换任何一种类型:

("oh my goodness" as any).ToFixed(1); // 还记得我说的健全性吗?
复制代码

strict 编译器选项启用了如下编译器设置,这些设置会使事情听起来更加合理:

  • --strictNullChecks
  • --noImplicitAny
  • --noImplicitThis
  • --alwaysStrict

还有 eslint 规则 @typescript-eslint/no-explicit-any

any 的泛滥会破坏你类型的健全性。

结论

必须重申,我是 TypeScript 爱好者,并且一直在平常工做中使用它,可是我确实认为它出现的时间还很短,并且类型还并不彻底合理。 Airbnb 声称 TypeScript 能够阻止 38% 的错误。我很是怀疑这个数字的准确性。 TypeScript 不会对现有的作法有良好的提升。我仍然必须编写尽量多的测试。你可能会不一样意,不过我一直在编写更多的代码,而且不得不去编写类型测试,同时仍然会遇到意外的运行时错误。

TypeScript 提供了基本的类型检查,但健全性和运行时类型检查不是它的目标,这使 TypeScript 在美好的世界和咱们所处的现状中采起折衷。

TypeScript 的亮点在于有良好的 IDE 支持,例如 vscode,若是咱们输入了错误的内容,将会得到很好的视觉反馈。

vscode中的TypeScript错误

vscode中的TypeScript错误

经过 TypeScript 还能够加强重构的功能,而且在对修改后的代码进行编译时,能够当即识别出代码的改变(例如方法签名的更改)。

TypeScript 启用了良好的类型检查,而且绝对要比没有类型检查或仅使用普通的 eslint 更好,可是我认为它还能够作更多的事情。对于那些想要更多的人来讲,还可以提供足够多的编译器选项。

欢迎关注前端公众号:前端先锋,免费领取 Vue、React 性能优化教程。

相关文章
相关标签/搜索