做者:Paul Cowanjavascript
翻译:疯狂的技术宅前端
原文:blog.logrocket.com/is-typescri…java
未经容许严禁转载ios
在开始以前,但愿你们知道,我是 TypeScript 爱好者。它是我在前端 React 项目和基于后端 Node 工做时的主要编程语言。但我确实有一些疑惑,因此想在本文中进行讨论。迄今为止,我已经用 TypeScript 写了至少三年的代码,因此 TypeScript 作得的确不错,并且知足了个人需求。git
TypeScript 克服了一些很难解决的问题,并成为前端编程领域的主流。 TypeScript 在这篇列出了最受欢迎的编程语言的文章中排名第七位。github
不管是否使用 TypeScript,任何规模的开发团队都应该遵循如下惯例:typescript
TypeScript 能够在这些基础之上增长额外的安全性,但我认为这在编程语言需求列表中应该排在后面。编程
我认为这多是 TypeScript 当前版本的主要问题,可是首先让我定义 健全 和 非健全 的类型系统。后端
健全的类型系统是可以确保你的程序不会进入无效状态的系统。例如,若是表达式中的静态类型为 string
,则在运行时,要保证在评估它时仅得到 string
。api
在健全的类型系统中,绝对不会在编译时或运行时产生表达式与预期类型不匹配的状况。
固然 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 的目标,所以这种愿望可能永远不会实现。例如在处理从 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错误
经过 TypeScript 还能够加强重构的功能,而且在对修改后的代码进行编译时,能够当即识别出代码的改变(例如方法签名的更改)。
TypeScript 启用了良好的类型检查,而且绝对要比没有类型检查或仅使用普通的 eslint 更好,可是我认为它还能够作更多的事情。对于那些想要更多的人来讲,还可以提供足够多的编译器选项。