写点什么

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

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

关注

评论 3 条评论

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

透过现象看Java AIO的本质 | 得物技术

得物技术

后端 Java】

加速国产化真替真用,京东云打造“多云多芯多活””数字基础设施

京东科技开发者

国产化 数字化建设 数字基础设施 国产化替代 京东云峰会

协同存储,为边缘计算创造更大价值

阿里云视频云

云计算 边缘计算

在昇腾平台上对TensorFlow网络进行性能调优

华为云开发者联盟

人工智能 华为云 昇腾 华为云开发者联盟 企业号 3 月 PK 榜

pix2pix3D:只需编辑标签,就能生成更逼真的三维图像

Zilliz

3 月 16 日晚 8 点,陪你一起从 CentOS 迁移到 OpenCloudOS!

OpenCloudOS

Linux

火山引擎DataLeap:一家企业,数据体系要怎么搭建?

字节跳动数据平台

大数据 数据架构 数据治理 数据研发 企业号 3 月 PK 榜

LED显示屏与LCD拼接屏的对比

Dylan

PC LCD1602液晶显示屏 LED显示屏

【总结】Java实现短信验证码

宙哈哈

Java 验证码

树莓派+阿里云IoT人脸识别场景实战——业务系统架构类

阿里云AIoT

Python JavaScript 物联网 对象存储 Web App开发

IDEA 插件最佳组合:JRebel+XRebel 热部署和接口分析优化,太爽了

Java你猿哥

Java Spring Boot ssm IDEA 热部署

LP流动性质押挖矿dapp系统开发分红模式定制

开发v-hkkf5566

C#滑动拼图验证码实现笔记

宙哈哈

C# html 验证码

“伯乐”流量调控平台工程视角 | 得物技术

得物技术

运营 架构-

火山引擎DataTester:A/B实验如何应用在抖音的产品优化流程中?

字节跳动数据平台

大数据 云服务 AB testing实战 ab测试 企业号 3 月 PK 榜

GPT-4问世;LLM训练指南;纯浏览器跑Stable Diffusion

OneFlow

人工智能 深度学习

分布式数据库架构路线大揭秘

华为云开发者联盟

数据库 后端 华为云 华为云开发者联盟 企业号 3 月 PK 榜

应用内支付服务现网、沙盒环境下常见关键事件的对比与总结

HarmonyOS SDK

HMS Core

深圳高新技术企业申请条件以及流程简单说明

行云管家

高新企业 高新技术 高新

高级数据库管理:SQLPro for SQLite激活版

真大的脸盆

数据库 Mac 数据库管理工具 数据库管理 Mac 软件

2023最新版Java面试八股文大全PDF版限时分享,含700道高频面试题

Java你猿哥

Java ssm Java 面试 面经 春招

狂野之心 The Wild at Heart for Mac (魔法森林冒险游戏)

互联网搬砖工作者

DockQuery | 成为信创产业“关键码”

BinTools图尔兹

数据库 信创产业 国产数据库工具 DockQuery

ChunJun 顺利晋级“2022 年中国开源创新大赛”决赛,并荣获“优秀开源项目/社区”奖项

袋鼠云数栈

开源

行云管家堡垒机客服电话是多少?谁知道?

行云管家

网络安全 数据安全 堡垒机 行云管家

如何提升运维的效率,可以用小程序试试

没有用户名丶

DNS、硬件、LVS、Nginx该如何搭配?

源字节1号

前端开发 后端开发 小程序开发,软件开发

强烈推荐!阿里架构师纯手写的大型分布式项目《凤凰架构》手册

Java你猿哥

Java 架构 微服务 微服务架构 面经

火山引擎数智平台VeDI 帮助智能投影仪更懂用户需求

字节跳动数据平台

营销数字化 业务增长 用户运营 企业号 1 月 PK 榜

得物榜单|全链路生产迁移及B/C端数据存储隔离

得物技术

运维 生产

介绍3种ssh远程连接的方式

华为云开发者联盟

后端 开发 华为云 华为云开发者联盟 企业号 3 月 PK 榜

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