HarmonyOS开发者限时福利来啦!最高10w+现金激励等你拿~ 了解详情
写点什么

TypeScript 被吹过头了?

  • 2020-03-24
  • 本文字数:2703 字

    阅读完需:约 9 分钟

TypeScript被吹过头了?

开始看本文之前,我希望读者朋友们知道我在很大程度上是一位 TypeScript 粉丝。在我的前端 React 项目和各种后端 Node 工作里,所使用的主要编程语言都是 TypeScript。我是这条船上的人,但也确实有一些疑惑,想在这篇文章中讨论一下。到目前为止,我已经使用 TypeScript 写了至少三年的代码,涉及的项目不计其数,因此可以说 TypeScript 的确是走在了正路上,或者说满足了某种需求。


TypeScript 克服了一些难以逾越的障碍,成为了前端编程领域的主流之选。TypeScript 在这篇文章列出的最受欢迎编程语言中排名第七。


无论你是否在使用 TypeScript,不管多大规模的软件团队都应遵循以下实践:


  • 精心编写的单元测试应在合理范围内覆盖尽可能多的生产代码;

  • 结对编程——另一双眼睛可以捕捉到的远不止语法错误;

  • 良好的同行评审流程——正确的同行评审可以找出许多机器无法捕获的错误;

  • 使用 eslint 之类的 linter。


TypeScript 可以在这些基础之上增加额外的一层安全性,但我认为这在编程语言的需求列表中是排在非常靠后的位置上的。

TypeScript 并非健全的类型系统

我认为这可能是 TypeScript 当前版本面临的主要问题,但首先,我来定义一下什么是健全(sound)和不健全(unsound)的类型系统。

健全

一个健全的类型系统能确保你的程序不会进入无效状态。例如,如果一个表达式的静态类型是 string,则在运行时,对它求值时你肯定只会得到一个 string。


在健全的类型系统中,永远不会在编译时 或运行时 出现表达式与预期类型不匹配的情况。


当然,健全性(soundness) 也是有级别的,具体含义可以斟酌。TypeScript 具有一定程度的健全性,并会捕获以下类型的错误:


// 类型 'string' 不能赋值给 类型 'number'const increment = (i: number): number => { return i + "1"; }
// 类型 '"98765432"' 的参数不能赋值给类型 'number' 的参数.const countdown: number = increment("98765432");
复制代码

不健全

关于 Typescript,以下事实是非常明确的:100%的健全性不是它的目标,并且 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 有一个健全的系统就能做到了。采用这种方法,意味着 TypeScript 在健全和不健全的阵营之间是在脚踩两只船。这种摇摆路线遇上 any 类型就更严重了,我将在后文提到这一点。我还是得编写尽可能多的测试,这让我感到沮丧不已。当我第一次开始使用 TypeScript 时,我错误地得出了一个结论,以为以后就用不着编写这么多单元测试了。


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


我理解为什么 TypesScript 会走这条路,并且有一个观点指出,如果类型系统的健全性得到 100%保证,那么 TypeScript 的采用率就不会那么高了。随着 dart 语言逐渐流行(因为 Flutter 现已广泛应用),这个看法也被证伪了。健全性是 dart 语言的一个目标,可以参考这里的讨论


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;虽然它很棒,但可能意味着你必须复制你的模型。

可怕的 any 类型和严格选项

any 类型就是字面意思,编译器允许任何操作或赋值。


TypeScript 往往在小事上表现很不错,但是人们习惯给花费时间超过 1 分钟的任何事物都来个 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 编译器时,可以立即识别出代码中断(例如方法签名的更改)。


TypeScript 带来了良好的类型检查,并且绝对比没有类型检查器或仅使用普通的 eslint 要更好,但是我认为它能更进一步。另外对于我们这种想要更多能力的用户来说,它应该可以提供足够多的编译器选项才是。

英文原文

Is TypeScript Worth It?


2020-03-24 08:584688
用户头像
小智 让所有人认同的文字称不上表达

发布了 408 篇内容, 共 388.9 次阅读, 收获喜欢 1980 次。

关注

评论 3 条评论

发布
用户头像
“TypeScript 处于脚踩两条船的状态,一只脚踏上了更好的那艘,一只脚还呆在现在的破船上,真是不幸。” 这句分析的太好了
2020-04-04 18:48
回复
用户头像
any是为了兼容那些烂糟js代码设计的,ts只是比js好,对比js已经省了很多事了
2020-03-28 19:56
回复
用户头像
1
2020-03-24 16:15
回复
没有更多了
发现更多内容

云原生训练营 -Week10

jjn0703

云原生训练营

ThinkPHP6+swoole+easywechat使用教程

CRMEB

《Mybatis 手撸专栏》第6章:数据源池化技术实现

小傅哥

Java 面试 小傅哥 mybatis 源码学习

微信小程序开发系列 (四) :微信小程序的页面跳转路由设计

汪子熙

微信小程序 微信 前端开发 微信开发 4月月更

基于语义感知SBST的API场景测试智能生成

华为云开发者联盟

测试 语义感知 SBST 动态修正 ODG图

2021最新一次Java面试,快手三面一轮游,如今已拿意向书

爱好编程进阶

Java 程序员 后端开发

[Day24]-[二叉树] 相同树

方勇(gopher)

LeetCode 二叉树 DFS BFS 数据结构算法

云原生训练营学习总结

arctec

别再用老版云效Projex项目协作了,该升级了

阿里云云效

阿里云 项目管理 研发团队 项目协作 项目协作工具

imazingAPP软件怎么安装到苹果手机电脑上面?

茶色酒

imazing

什么是瀑布开发?适用于哪些场景?有哪些瀑布开发管理系统?

爱吃小舅的鱼

丙午篇 「準佛」 《「內元宇宙」聯載》

因田木

陰陽五行

20 数据存储服务器集群的伸缩性设计

爱好编程进阶

Java 程序员 后端开发

小平邦彦:树懒style的世界一流数学家

图灵教育

数学 数学史 数学家

字节跳动构建Data Catalog数据目录系统的实践

字节跳动数据平台

数据库 字节跳动 数据治理 数据目录

苹果手机怎么恢复备份?iOS备份恢复教程

茶色酒

苹果手机备份

聊聊Kotlin中的lambda

北洋

kotlin Andriod 4月月更

18 张图,一文了解 8 种常见的数据结构

爱好编程进阶

Java 程序员 后端开发

2021最新Java学习路线,自学者的福利

爱好编程进阶

Java 程序员 后端开发

Docker下,极速体验pinpoint1.6.3

程序员欣宸

Java 分布式 4月月更

微服务与领域驱动设计,架构实践总结

架构 微服务 领域驱动设计 软件架构

想学习算法交易的工程师们,机会来啦~

非凸科技

rust 招聘 基金 量化交易 算法交易

华为云大咖带你玩转云原生基础设施之K8s

坚果

4月月更

Go 语言入门很简单:正则表达式

宇宙之一粟

正则表达式 Go 语言 4月月更

C语言总结_数组全方位练习

DS小龙哥

4月月更

大数据培训Spark SQL底层执行流程

@零度

Sparksql 大数据开发

一种很爽的学习方法,被我Get到了!

博文视点Broadview

imazing是什么软件?

茶色酒

imazing

Spark SQL 字段血缘在 vivo 互联网的实践

vivo互联网技术

大数据 spark Sparksql 数据处理

13W字!2021最新发布互联网大厂高频面试技术点!

爱好编程进阶

Java 程序员 后端开发

自己动手写 Docker 系列 -- 6.5 启动时给容器配置网络

Go Docker 4月月更

TypeScript被吹过头了?_GMTC_Paul Cowan_InfoQ精选文章