写点什么

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:584731
用户头像
小智 让所有人认同的文字称不上表达

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

关注

评论 3 条评论

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

九年程序员生涯,我的一些经验教训,offer来了java面试百度云版

Java 程序员 后端

京东十年T8架构师手撕MySQL:手写666页核心知识,超85,java在线编译器实现原理

Java 程序员 后端

从Mybatis源码到Spring动态数据源底层原理分析系列一、Mybatis初始化源码浅析

Java 程序员 后端

五位阿里大牛联手撰写的《深入浅出Java多线程》,java开发视频直播

Java 程序员 后端

什么?这个岗位薪资秒杀一众程序员?,java技术面试常见问题

Java 程序员 后端

互联网架构“高并发”到底怎么玩,用Elasticsearch搞定日均1亿订单查询

Java 程序员 后端

五分钟看穿Java并发相关概念,并发原来如此简单,linux学习步骤

Java 后端

人性的弱点-读书笔记,java面试题大汇总小山博客

Java 程序员 后端

从 0 到 1,带你解剖 MVP 的神秘之处,并自己动手实现 MVP !

Java 程序员 后端

为什么阿里巴巴不建议在for循环中使用”+,java编程技术开发题库和答案

Java 程序员 后端

二十不惑的年纪,竟4面拿下字节跳动offer,我简直走了,jdk和jre区别面试题

Java 程序员 后端

二叉树的各种算法面试题及答案解析,linux基础教程第二版pdf

Java 程序员 后端

京东三面被惨虐:索引+数据库,kafka入门与实践epub

Java 程序员 后端

什么神仙笔记!阿里P9用39实例+1项目讲明白了Spring Cloud家族

Java 程序员 后端

从Mybatis源码到Spring动态数据源底层原理分析系列二、Mybatis执行器源码分析

Java 程序员 后端

五分钟带你了解Seata分布式事务,java基础菜鸟教程txt

Java 程序员 后端

互联网寒冬下!疫情又遭滑铁卢!从裁员到斩获新offer我经历了什么

Java 程序员 后端

Vue进阶(幺伍伍):vue-resource 拦截器 interceptors 使用

No Silver Bullet

Vue vue-resource 10月月更

京东T7架构师手写的10万字Spring Boot详细学习笔记+源码免费下载

Java 程序员 后端

京东面试,这个问题让我与50万擦肩而过,帮忙看看,mysql下载教程window10

Java 程序员 后端

什么是 MySQL 全局锁、表锁、行锁,Java高级开发岗必问知识点

Java 程序员 后端

什么是接口的幂等性,如何实现接口幂等性?,java微服务架构视频下载

Java 程序员 后端

从Java小白到拿到30k offer,分享自己的学习路程,java基础案例教程pdf百度云

Java 程序员 后端

为什么说简历很重要?怎么才能写出一份完美的简历?这篇文章你值得拥有

Java 程序员 后端

二面余额宝(交叉面),mybatis功能架构

Java 程序员 后端

京东前端面经(123面详细),进阶学习工作最全指南

Java 程序员 后端

今年,我在字节跳动面试了九次【已意向书,mongodb入门pdf

Java 程序员 后端

今日头条一面:十道经典面试题解析,Redis如何实现高可扩展

Java 程序员 后端

京东T9裂墙力荐的82万字spring cloud微服务和分布式系统实践文档

Java 程序员 后端

人到中年的焦虑,Java面试第一问就是做过什么最有难度的项目

Java 程序员 后端

人工智能 - 语音识别的技术原理是什么,Java理论知识思维导图

Java 程序员 后端

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