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;借助于同…

mysql内连接的自连接_mysql 内连接、外连接、自连接

一)内连接(等值连接)&#xff1a;查询客户姓名&#xff0c;订单编号&#xff0c;订单价格---------------------------------------------------select c.name,o.isbn,o.pricefrom customers c inner join orders owhere c.id o.customers_id;-------------------------------…

关于ASP.NET MVC

我是否要学习一下ASP.NET MVC呢&#xff1f;因爲从它刚发布的时候就已经初步的学习了一下&#xff0c;可是一直没有坚持下来。不过心里对于这份惦记&#xff0c;又让我始终放不下&#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;国内很多公司也开始重视面向所有移动设备的网站…

mysql适配器_MySQL适配器PyMySQL详解

import pymysqlimport datainfoimport time#获取参数host datainfo.hostusername datainfo.usernamepassword datainfo.passworddatabase datainfo.dbprint()#测试数据库连接def testconnect():#打开数据库链接db pymysql.connect(host,username,password,database)#使用c…

获取当前Tomcat实例的端口

有时需要在当前代码中获取当前Server实例的端口号, 通过HttpServletRequest请求可以, 但有时也需要在没有请求的情况下获取到端口号. 用以下方法是可以获取到的: public int getHttpPort() {try {MBeanServer server;if (MBeanServerFactory.findMBeanServer(null).size() >…

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

大家好&#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 …

程序员,当你写程序写累了怎么办。

记得泡泡网的CEO李想说过这样一句话&#xff0c;大体就是&#xff1a;做一件事情&#xff0c;一开始是兴趣使然&#xff0c;然而当三分钟热度过去之后&#xff0c;就要靠毅力支撑自己来完成它。至少我到现在是能非常深刻的体会这句话。一开始再怎么喜欢做一件事&#xff0c;要想…

mysql 导致iis 假死_解决IIS无响应假死状态

1 查看服务器iis的w3wp.exe对应的应用程序池在IIS6下&#xff0c;经常出现w3wp的内存占用不能及时释放&#xff0c;从而导致服务器响应速度很慢。今天研究了一下&#xff0c;可以做以下配置&#xff1a;1、在IIS中对每个网站进行单独的应用程序池配置。即互相之间不影响。2、设…

Swift 5将强制执行内存独占访问

Swift 5将带来改进的Swift程序内存安全性&#xff0c;在程序的其他部分修改变量时&#xff0c;不允许通过其他变量名来访问这些变量。这个变更对现有应用程序的行为和Swift编译器本身都有重要影响。Swift 5将带来改进的Swift程序内存安全性&#xff0c;在程序的其他部分修改变量…

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版项目截图功能列表首页分类…

前端菜鸟笔记 Day-5 CSS 高级

文章大纲来源&#xff1a;【Day 5】CSS 高级 CSS 选择器CSS 拓展CSS 单位CSS 参考手册CSS 选择器 内容引用&#xff1a;CSS 选择器 元素选择器 html { ... } 复制代码选择器分组 h2, p { ... } 复制代码类选择器 .important { ... } p.warning { ... } .important.warning { .…

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

不得不说&#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 不过对于我这种低手来说&…