TypeScript 原来可以这么香?!

先问一个问题,JavaScript有几种数据类型?

numberstringbooleannullundefinedsymbolbigintobject

其中 bigint 是 ES2020 新增的数据类型,而早在 TS3.2 时便成为 TS 的标准,其实还有好多 ES+ 标准是 TS 率先提出的,可见 TS 在很多方面是走在了 ES 前列。

TypeScript又新增了多少种数据类型?

anyunknownenumvoidnevertuple...

其实 TypeScript 更重要的是通过 interface 和 type 赋予了用户自定义数据类型的能力,使数据在流转的过程中始终能轻易被用户掌握。

Bug 绞杀者

TypeScript 可以让我们的程序写的更健壮,并且更好维护,丰富的代码的提示功能也能够提高我们的开发效率以及降低协作成本,并帮助我们在程序的编译阶段检查出许多因为类型的原因导致的低级错误。

那么我们先来看下 JavaScript 项目最常见的十大错误:

这些低级的错误是不是耳熟能详?为了解决这些问题,占用了我们大量的 debug 和 google 的时间。程序员最烦的两件事:一件是自己写完代码还要写注释文档,一件是别人的代码没留下任何注释文档。

在我们的日常开发中,很常见的是调用或者修改别人写的函数。但是如果别人的代码没留下任何注释的话,我们就要硬着头皮去看里面的逻辑。

假如我们优化了一个底层类库的参数类型,而不知道有多少处引用,在提交代码前,是不是内心在打鼓,心里没底呢?毕竟我们都不想被杀了祭天吧...

笔者前不久就因为一个很小的问题 debug 了很长时间,我们来看一下,因内部代码所以经过脱敏处理。

因为 JavaScript 太灵活了,它允许你对参数做任何操作,所以之前同学写的时候直接将 userId 挂载到入参 query 上面,后面用到处解构也就有了相应的 userId 属性。但是笔者对这部分业务不熟悉,以为入参 query 不会改变,所以 debug 了好久。

而假如我们使用的是 TypeScript ,我们会定义好入参 query 的结构,为 query 添加 userId 属性的时候就能有报错提示,可以规避我们以前的写法,换成更健壮性的写法。

另外 TypeScript 还能实现自文档化,使后面维护的同学也能轻松的接手。

我们可以通过/** */来注释 TypeScript 的类型,当我们在使用相关类型的时候就会有注释的提示,这个技巧可以帮助我们节约翻文档或者跳页看注释的时间,在团队内合理的推广使用,能够极大的提高我们的开发效率。

优化程序性能

合理的使用 TypeScript ,再凭借 V8 引擎可以帮助我们极大的优化程序的性能。

在 V8 引擎下,引入了 TurboFan 编译器,它会在特定的情况下进行优化,将代码编译成执行效率更高的 Machine Code,这个编译器虽然不是 JavaScript 必须的,但是却能够极大的提高代码执行效能。

我们知道, JavaScript 代码首先会解析为抽象语法树(AST),然后会通过解释器或者编译器转化为 Bytecode 或者 Machine Code。其中 Ignition 负责将 AST 转化为 BytecodeTurboFan 负责编译出优化后的 Machine Code,并且 Machine Code 在执行效率上优于 Bytecode

那么问题来了,什么情况下的代码会编译为 Machine Code 呢?JavaScript 是一门动态类型语言,并且有一大堆的隐式类型转换规则,比如数字相加、字符串相加、对象和字符串相加等等。这样的情况也就势必导致了内部要增加很多的判断逻辑,降低运行时的效率。

function test (x) {return x + x;
}test(1)
test(2)
test(3)
test(4)

对于上面这段代码来说,如果一个函数被多次调用并且参数一直传入 number 类型,那么 V8 引擎就会认为该段代码可以编译为 Machine Code,因为我们固定了类型,不需要再执行很多判断逻辑了。

但是一旦我们传入的参数类型改变,那么 Machine Code 就会被 DeOptimized Bytecode,这样就会造成性能上的损耗。所以如果我们希望代码能尽可能多的编译为 Machine Code 并且 DeOpimized 的次数减少,那么我们就应该尽可能的保证传入的类型一致。

但是你可能还有一个疑问,优化前后的性能提升到底是怎么样的呢?有什么数据支撑么?

const v8 = require('v8-natives');
const { performance, PerformanceObserver } = require('perf_hooks')function test(x, y) {return x + y
}const obs = new PerformanceObserver((list, observer) => {console.log(list.getEntries())observer.disconnect()
})
obs.observe({ entryTypes: ['measure'], buffered: true })performance.mark('start')let number = 10000000// 不优化代码
v8.neverOptimizeFunction(test)while (number--) {test(1, 2)
}performance.mark('end')
performance.measure('test', 'start', 'end')

我们接下来使用 performance 这个 API 测量一下代码的执行时间,这个 API 经常用于一些性能测试,还可以用来测量各种网络连接中的时间消耗,并且也可以在浏览器中使用。

我们实际运行一下代码发现,经过优化后的代码执行时间只需要 10ms,但是不优化的代码却是前者的十二倍,高达了 124ms

在这个案例中,我们能够看到 V8 的性能优化十分强大,只需要我们符合一定规则书写代码,引擎底层就能帮助我们自动优化代码。

那么为了让 V8 优化代码,我们要尽可能的保证传入参数的类型一致,而这,也正是 TypeScript 带给我们的好处之一,借助 TypeScript,可以强迫我们思考定义好每一处的变量类型,让每一处的变量类型都做到最小可控。使 V8 可以自动将我们的代码优化成 Machine Code。

所以我们可以设想一下,未来怎么样凭借 TypeScript,让 V8 进一步得到优化。

  • 1、使用 TypeScript 编程,遵循严格的类型化编程规则,摒弃 AnyScript。

    2、构建的时候将 TypeScript 直接编译为 Bytecode,而不是 JavaScript 文件,这样运行的时候就省去了 Parse 以及生成 Bytecode 的过程。

    3、运行的时候,需要先将 Bytecode 编译为对应 CPU 的汇编代码。

    这样由于采用了类型化的编程方式,有利于编译器优化所生成的汇编代码,省去了很多额外的操作。

未来的潮流

很多的前端底层库都在从 JavaScript 向 TypeScript 迁移,像我们熟悉的 Angular 和 Vue3 已经全面用 TypeScript 重构代码,在 ECMAScript 标准推出静态类型检查之前,TypeScript 是解决当下问题的最佳实践。

stackoverflow 统计的2020年最受开发者喜欢的语言,TypeScript 已排到第二名。

但是很多同学一开始可能不太喜欢使用 TypeScript,人们在接触一个新事物的时候往往会出现本能的抗拒,因为不太确定这个新的事物能够带给我们什么开发体验的提升。

可能有些同学抱着试试看的态度去尝试使用了一下,但却发现这个东西巨难用,常常代码死活编译不过去,各种类型不匹配。一些底层类库的不完善,没有很好的支持 TypeScript 也会增加我们开发的上手难度。

这也难怪,因为 JavaScript 是一门动态弱类型语言,对变量的类型非常宽容,而且不会在这些变量和它们的调用者之间建立结构化的契约,JavaScript 带给我们极大的灵活性。

一想到以前快乐的时光,可能有的同学直接弃疗,重新回到 JavaScript 的怀抱;可能有的同学转而去写 AnyScript,遇事不决上 any,但这是非常差的编程习惯!假如我们写 AnyScript,我们就失去了 TypeScript 的意义,为此我们还要多写好多的冗余代码,影响开发效率。

其实使用 TypeScript 进行开发,理论上我们99.99%的情况都不应该使用 any,当我们想要使用 any 的时候,就多去思考,查阅资料,仔细推敲,只要我们能够不断忍住不写 any,多写 TypeScript,写到如臂指使,后面你会发现自己的开发效率在不断提升。

实用小技巧

1. 巧用 keyof

假如我们要实现一个 getValue 函数,作用是根据传入的 object 和 key 获取 object 上 key 所对应的属性值 value,一开始的 JavaScript 版本可能是这样的。

const data = {a: 3,hello: 'world'
}
function getValue(o, name) {return o[name]
}

可以看到这就是 JavaScript 的弊端,我们对传入的 o 和 name 都没有约束,对这一切都是未知的,人的恐惧就源自对未知事物的不确定性。尤其如果这是一个底层类库,我们不知道有多少人去调用,那这个风险就更加增大了,所以我们可能用 TypeScript 这样去改造它。

function getValue(o: any, name: string) {return o[name]
}

但这样写貌似也有很多问题,函数返回值的类型变成了 any,失去了 TypeScript 类型校验的功能,让类型重新变的不可控,name 类型固然是 string,但还是太宽泛了,实际上,name 的值只能是o的键的并集,而且如果我们将 name 拼写错误,TypeScript 也不会帮我们报错提示。这个时候我们可以使用 keyof 结合泛型来加强 getValue 函数的类型功能。

function getValue<T extends object, K extends keyof T>(o: T, name: K): T[K] {return o[name]
}

2. 接口智能提示

interface Seal {name: string;url: string;
}
interface API {"/user": { name: string; age: number; phone: string };"/seals": { seal: Seal[] };
}
const api = <URL extends keyof API>(url: URL): Promise<API[URL]> => {return fetch(url).then((res) => res.json());
};

借助泛型以及泛型约束,我们可以实现智能提示的功能,不光接口名可以智能提示,接口返回也可以智能提示。当我们输入 api 的时候,其会自动将 API interface 下的所有 key 提示给我们,当我们输入某一个 key 的时候,其会根据 key 命中的 interface 定义的类型,然后给予类型提示。这在统一接口管理方面有很大的用处,可以帮助我们面向接口编程。

3. 巧用类型保护

interface User {name: string;age: number;occupation: string;
}
interface Admin {name: string;age: number;role: string;
}
export type Person = User | Admin;
export const persons: Person[] = [{name: 'Max Mustermann',age: 25,occupation: 'Chimney sweep'},{name: 'Jane Doe',age: 32,role: 'Administrator'},{name: 'Kate Müller',age: 23,occupation: 'Astronaut'},{name: 'Bruce Willis',age: 64,role: 'World saver'}
];
export function logPerson(person: Person) {let additionalInformation: string;if (person.role) {additionalInformation = person.role;} else {additionalInformation = person.occupation;}console.log(` - ${person.name}, ${person.age}, ${additionalInformation}`);
}
persons.forEach(logPerson);

我们可以看到,当我们定义了两种 Person 类型:User 和 Admin,而在使用的时候是比较宽泛的 Person,那我们就不能直接使用 User 或者 Admin 的特有属性 role 或者 occupation。因为 TypeScript 没有足够的信息确定 Person 究竟是 User 还是 Admin。

一种方法是使用类型断言,显示的告诉 TypeScript,person 就是 Admin 类型或者就是 User 类型,但是这样做一方面不够优雅,要在每一处都加上断言;另一方面滥用断言也会让我们的代码变得不可控,不能让 TypeScript 帮助我们进行合理的类型推断。像双重断言可以规避掉 TypeScript 的类型检查机制也是与 any 一样,要尽可能去避免的。

正确的做法是使用类型收缩,比如使用 is,in,typeof,instanceof 等,使得 TypeScript 能够 get 到当前的类型,假如 person 上面有 role 属性,TypeScript 就可以推断出 person 就是 Admin 类型,创建类型保护区块,在当前的代码块按照 Admin 类型处理,代码也简洁了很多。

同样是这个例子,我们再改造一下,通过两个函数来判断 person 的具体类型是 Admin 还是 User。但是很不幸,TypeScript 依然不能很智能的知道 person 在第一个代码块里是 Admin 类型,在第二个代码块里是 User 类型。

这个时候我们就要改造一下 isAdmin 和 isUser 的函数返回,创建用户自定义的类型保护函数,显式的告诉 TypeScript,函数返回为 true 时,指定 person 的类型确定为 Admin 或者 User,这样 TypeScript 就知道 person 的确定类型了,这就是类型位词。

4.常用的高级类型

这其实涉及到了类型编程到概念,简而言之,我们平时写代码是对值进行编程,而类型编程是对类型进行编程,可以利用 keyof 对属性做一些扩展,省的我们要重新定义一下接口,造成很多冗余代码。

这些高级类型在日常编程中有非常广泛的使用,尤其 Partial 可以将所有的属性变成可选的,如在我们日常的搜索逻辑,我们可以根据单一条件搜索,也可以根据组合条件搜索。Omit 可以帮助我们复用一个类型,但是又不需要此类型内的全部属性,当父组件通过 props 向下传递数据的时候,可以剔除一些无用的类型。

Record 也是一个比较常用的高级类型,可以帮助我们从 Union 类型中创建新类型,Union 类型中的值用作新类型的属性。当我们拼写错误,或者漏写一些属性,或者加入了没有预先定义的属性进去,TypeScript 都可以给我们很友好的报错提示。

type Partial<T> = {[P in keyof T]?: T[P];
};
type Required<T> = {[P in keyof T]-?: T[P];
};
type Pick<T, K extends keyof T> = {[P in K]: T[P];
};
type Exclude<T, U> = T extends U ? never : T;
// 相当于: type A = 'a'
type A = Exclude<'x' | 'a', 'x' | 'y' | 'z'>
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
type Record<K extends keyof any, T> = {[P in K]: T;
};interface User {id: number;age: number;name: string;
};
// 相当于: type PartialUser = { id?: number; age?: number; name?: string; }
type PartialUser = Partial<User>
// 相当于: type PickUser = { id: number; age: number; }
type PickUser = Pick<User, "id" | "age">
// 相当于: type OmitUser = { age: number; name: string; }
type OmitUser = Omit<User, "id">type AnimalType = 'cat' | 'dog' | 'frog';
interface AnimalDescription { name: string, icon: string }
const AnimalMap: Record<AnimalType, AnimalDescription> = {cat: { name: '猫', icon: ' '},dog: { name: '狗', icon: ' ' },forg: { name: '蛙', icon: ' ' }, // Hey!
};

5.巧用类型约束

在 .tsx 文件中,泛型可能会被当作 jsx 标签

const toArray = <T>(element: T) => [element]; // Error in .tsx file.

加 extends 可破

const toArray = <T extends {}>(element: T) => [element]; // No errors.

TypeScript 还可以给一些缺乏类型定义的第三方库定义类型,找到一些没有 d.ts 声明的开源库,为开源社区贡献声明文件。

学习参考

  1. https://www.typescriptlang.org/docs/handbook/release-notes/overview.html 官方各个版本文档

  2. https://github.com/microsoft/TypeScript/projects/9 官方讨论

  3. https://github.com/microsoft/vscode VS Code 是 TypeScript 编写的,毫无疑问也是学习的好地方

  4. https://basarat.gitbook.io/typescript/getting-started TypeScript Deep Dive

  5. https://github.com/typescript-exercises/typescript-exercises TypeScript 优秀的练习题

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/275903.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java8新特性stream深入解析

2019独角兽企业重金招聘Python工程师标准>>> 继续java8源码的发烧热&#xff0c;越看越是有充实的感觉。 数据时代下的产物 Java顺应时代的发展推出的高效处理大量数据能力的api&#xff0c;它专注于对集合对象进行各种非常便利、高效的聚合操作&#xff0c;借助于同…

版式设计与创意 pdf_恋爱与版式

版式设计与创意 pdfSince its beginnings, Libération has been characterized by a very distinctive use of typeface, to such an extent that Libé has put its mark on fonts from across different eras, appropriating these in a certain way.小号因斯它的起点&…

移动网站开发——标记语言

移动互联网被称为“第五次科技革命”&#xff0c;而随着iPhone和Android等智能手机的日渐流行和iPad等平板电脑的出现&#xff0c;移动互联网的潜力和趋势也愈发显现&#xff0c;针对移动设备的网站开发越来越受到关注&#xff0c;国内很多公司也开始重视面向所有移动设备的网站…

前端技术未来三年前瞻性思考

大家好&#xff0c;我是若川。今天推荐云谦大佬的一篇好文章&#xff0c;值得收藏。点击下方卡片关注我、加个星标&#xff0c;或者查看源码等系列文章。学习源码整体架构系列、年度总结、JS基础系列习惯从业务场景、用户体验、研发速度、维护成本四个维度来看框架等前端技术&a…

微信临时素材接口_在接口中表达临时性

微信临时素材接口When interacting with today’s graphic user interfaces (GUI), we experience a sense of realism. As of now, certain aspects of realism (for example animations) create the appearance that user interface graphics behave in accordance with the …

GitHub 支持上传视频文件啦!

大家好&#xff0c;我是若川。今天周六&#xff0c;分享一篇热点新闻。文章较短&#xff0c;预计5分钟可看完。近日 Github 宣布支持了视频上传功能&#xff0c;意味着&#xff0c;大家在提 issue 时可以携带视频了&#xff0c;这极大地提高了开发者和维护者的效率&#xff0c;…

ui设计 网络错误_UI设计人员常犯的10个错误

ui设计 网络错误重点 (Top highlight)1.不考虑范围 (1. Disregarding scope)It’s not uncommon for designers to introduce features that will overcomplicate the development process while bringing no additional value to the application. Focusing on the business o…

小程序 node.js mysql_基于Node.js+MySQL开发的开源微信小程序B2C商城(页面高仿网易严选)...

高仿网易严选的微信小程序商城(微信小程序客户端)界面高仿网易严选商城(主要是2016年wap版)测试数据采集自网易严选商城功能和数据库参考ecshop服务端api基于&#xff2e;ode.jsThinkJSMySQL计划添加基于Vue.js的后台管理系统、PC版、&#xff37;ap版项目截图功能列表首页分类…

推荐几个干货超多助你成长的前端大佬

不得不说&#xff0c;如今比前些年学习资料多很多了。现在的前端公众号也挺多的&#xff0c;这里推荐几个前端大佬运营的公众号&#xff0c;都是聚焦前端垂直领域的优质公众号&#xff0c;关注这些公众号至少可以&#xff1a;1、了解现在前端技术发展情况和未来发展趋势&#x…

背景图片_背景

背景图片A designer’s journey is one that’s littered with many portfolios. Many of which have been reduced to a mere 404 error page, an abandoned link or another archive in the folders. Recently, while updating my portfolio, all the forgotten versions tha…

解决《Mobile绘制背景图片》中的问题

与PC平台的开发相比&#xff0c;Mobile的开发麻烦了许多&#xff0c;至少这是我的感觉 。 谢谢&#xff0d;&#xff0d;“ Fly Pig(^^)” 的文章《Mobile开发(绘制背景图片) 》 http://www.cnblogs.com/Bright-Liang/archive/2009/06/11/1501309.html 不过对于我这种低手来说&…

mysql 5.6.31 winx64_详解介绍MySQL5.6.31winx64.zip安装配置的图文教程

这篇文章主要介绍了MySQL5.6.31 winx64.zip 安装配置教程详解,非常不错&#xff0c;具有参考借鉴价值&#xff0c;需要的朋友可以参考下#1. 下载##2.解压到本地 修改必要配置my*.ini#3.mysql installadmin模式启动cmdcd mysql目录/bin执行安装&#xff1a; mysqld -install启动…

如何使用Gitbook创建html技术文档

故事背景&#xff1a;很多时候对外发布的产品需要一份html格式的文档 首先要了解的是 word直接转成html是不现实的&#xff0c;需要通过md文件来转换。 我们本节课讨论的Gitbook即是在MD基础上进行操作的。 所以任务一&#xff1a;安装typora软件&#xff0c;用于编辑md文件&am…

徒手撸了个markdown笔记平台

大家好&#xff0c;我是若川。今天分享一篇markdown笔记平台的项目文章。点击下方卡片关注我、加个星标&#xff0c;或者查看源码等系列文章。学习源码整体架构系列、年度总结、JS基础系列一、前言作为开发者&#xff0c;我觉的用markdown写文档是一件很酷的事情。在之前&#…

This week I’ve been forging background illustrations for my website, epdillon.com (launching soon). I’ve been using Adobe Illustrator to do all the major casting, dabbling in Figma only to temper the colours. Fresh from the design furnace, my hands are a…

pyqt控件显示重叠_Python编程:一个不错的基于PyQt的Led控件显示库,建议收藏学习...

1、控件说明在Github上&#xff0c;偶然发现了一个基于PyQt5的第三方Led指示灯控件库&#xff0c;使用起来非常方便&#xff0c;控件外观也比较漂亮&#xff0c;更难能可贵的是作者源代码写得比较简洁&#xff0c;仅仅才约200行左右&#xff0c;可以作为一个在PyQt中写自定义控…

编写高质量可维护的代码:优雅命名

大家好&#xff0c;我是若川。今天分享一篇关于如何命名更优雅的文章。点击下方卡片关注我、加个星标&#xff0c;或者查看源码等系列文章。学习源码整体架构系列、年度总结、JS基础系列本文首发于政采云前端团队博客&#xff1a;编写高质量可维护的代码&#xff1a;优雅命名ht…

powerpoint技巧_几乎每个PowerPoint都烂鸡蛋

powerpoint技巧Almost 20 years ago — 2001! — Seth Godin wrote an ebook called Really Bad Powerpoint (and how to avoid it). In that book he detailed all of the things that tend to go wrong in slide presentations.差不多20年前-2001年&#xff01; —塞斯戈丁(S…

认识mysql总结_从根上理解Mysql - 读后个人总结1-搜云库

初识 MySQL通信介绍MySQL 也是典型的 C / S 模型&#xff0c;分为客户端及服务端&#xff0c;服务端一般部署在远端服务器中&#xff0c;也可以部署至本地&#xff0c;然后客户端跟服务端通信则可以使用依赖网络的 TCP 长连接或 Unix-like 的系统下可以使用 Socket文件的形式通…

面试官问:你在项目中做过哪些安全防范措施?

大家好&#xff0c;我是若川。今天分享一篇安全相关的文章。点击下方卡片关注我、加个星标&#xff0c;或者查看源码等系列文章。学习源码整体架构系列、年度总结、JS基础系列如果你被面试官问到这个问题&#xff0c;不要急于描述自己遇到的问题以及如何处理的&#xff0c;你得…