范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

TypeScript基础学习之泛型和extends关键字

  越来越多的团队开始使用 TS 写工程项目, TS 的优缺点也不在此赘述,相信大家都听的很多了。平时对 TS 说了解,仔细思考了解的也不深,借机重新看了 TS 文档,边学习边分享,提升对 TS 的认知的同时,也希望能在平时的工作中能用上,少写一点 any。
  泛型
  A major part of software engineering is building components that not only have well-defined and consistent APIs, but are also reusable. Components that are capable of working on the data of today as well as the data of tomorrow will give you the most flexible capabilities for building up large software systems.
  In languages like C# and Java, one of the main tools in the toolbox for creating reusable components is generics, that is, being able to create a component that can work over a variety of types rather than a single one. This allows users to consume these components and use their own types.
  我们工作的大部分内容是构建组件,定义有效、一致并且可复用的API很重要。组件能够处理当前的数据,又能考虑兼容很多未来的数据,这样的组件能提高工作效率,也能在构建软件系统的时候提供非常灵活的能力。
  ▐基本使用
  通过泛型可以定义通用的数据结构,增加 TypeScript 代码中类型的通用性。
  处理函数
  先看一个具体的例子,感受一下泛型的应用。
  首先定一个 log 函数,功能很简单把传入的参数直接 return 就行,函数参数类型是 string,那么返回值也是 string 类型。
  function log(arg: string): string {     return arg; }
  当其他地方也想使用这个函数,但是参数入参数类型是 number,这个时候我们也许可以这么做:
  function log(arg: string | number): string | number {     return arg; }
  当有更多的地方要使用这个函数的时候,那这个函数的参数类型定义和返回值类型定义将会变得无比冗长,或者可能就直接使用 any 来解决,当使用 any 的时候就失去了使用 TS 最初的初心了。
  这个时候泛型出现了,能解决输入输出一致的问题,我们可以这样写:
  function log(arg: T): T {     return arg; }
  这个 log 函数通过泛型来约束输入输出一致性的问题,把动态的泛型类型抛给函数的使用者,我们只需要保证输入输出的一致性就可以,还能支持任何类型。泛型中的 T 就像一个占位符,或者说一个变量,在使用的时候把定义的类型像参数一样传入就可以了。
  我们在使用的时候可以有两种方式指定类型。第一,是直接定义要使用的类型,第二,是默认 TS 的类型推断,TS自动推导出要传入的类型:
  log("log")  // 定义 T 为 string   print("log")  // TS 的类型推断,自动推导类型为 T 的类型为 string
  默认参数
  在 JS 中对于一个函数入参,可以使用默认参数来简化当没有传参数时候的默认值,在 TS 中我们可以这样使用:
  function log(arg: T): T {     return arg; }
  当没有传泛型参数的时候 T 的默认类型是 string 类型,就如果 JS 中的函数默认参数类似的用法
  多个参数
  当函数中有多个参数的时候可以这样使用:
  function log(type: T, info: U): [T, U] {     return [type, info]; }
  通过在泛型定义多个对应位置的类型就可以获取到相应的泛型传参来对输入输出做一些处理。
  函数返回值
  泛型不仅可以很方便地约束函数的参数类型,还可以用在函数执行副作用操作的时候。发送请求是我们使用的很多的操作,我们会有一个通用的发送请求的异步方法,请求不同的 url 会返回不同的类型数据,那么我们可以这样使用。
  function request(url: string): Promise {     return fetch(url).then(res => res.json()) }   interface IUserInfo {   name: string;   age: number;   avatar: string;   gender: "male" | "female";   city: string; }   request("/getuserinfo").then(res => {   console.log(res) });
  这个时候返回的数据 TS 就会识别出 res 的类型,对解下来的代码编写会有很大帮助。
  ▐应用
  上面的一些小例子,我们对泛型有了一些了解,在平时我们可以这样使用。
  泛型约束类
  定一个栈,有出栈和入栈两个方法,规定了出栈和入栈的元素类型必须一致。
  class Stack {   private data: T[] = []     push(item:T) {       return this.data.push(item)   }     pop(): T | undefined {       return this.data.pop()   } }
  在使用的使用传入类型生成实例,在调用对应的出栈和入栈方法的时候入参数类型不对就会报错,从而约束了栈的元素类型。
  const test1 = new Stack() const test2 = new Stack()
  泛型约束接口
  function request(url: string): Promise {     return fetch(url).then(res => res.json()) }   interface IUserInfo {   name: string;   age: number;   avatar: string;   gender: "male" | "female";   address: T; }   request>("/getuserinfo").then(res => {   console.log(res) });
  这样使用 IUserInfo 的 address 的类型就是 string,让 interface 更灵活。
  Pick
  Pck 是 TS 内置的函数,作用是挑选出对象类型 T 中 U 对应的属性和类型,创建一个新的对象类型。
  type Pick = {     [P in K]: T[P]; };   interface IUserInfo {   name: string;   age: number;   avatar: string;   gender: "male" | "female"; }   type Test = Pick // { name: string }
  Omit
  与Pick的功能是互补的,挑选出对象类型 T 中不在 U 中的属性和类型,创建一个新的对象类型。
  type Omit = Pick>;   interface IUserInfo {   name: string;   age: number;   avatar: string;   gender: "male" | "female"; }   type Test = Omit // { age: number }
  ▐总结
  泛型,从字面上来理解,就是一般的,广泛的,具有通用性的。
  泛型是指在定义函数、接口或类的时候,不预先指定具体类型,而是在使用的时候再指定类型。
  泛型中的 T 就像一个占位符、或者说一个变量,在使用的时候可以把定义的类型像参数一样传入,它可以原封不动地输出。
  extends
  本文主要整理 extends 关键字在 typescript中的相关用法,平时在看一些复杂的 TS 类型的时候经常会看到使用 extends 这个关键字
  ▐继承类型
  TS 中的 extends 关键字第一个用法可以理解成 JS 中相似的用法继承类型。
  interface IName {  name: string; }   interface IGender {   gender: string; }   interface IPerson extends IName, IGender {   age: number; }   const corgi: IPerson = {   name: "corgi",   gender: "female",   age: 18, }
  以上示例中,IName 和 IGender 两个接口,分别定义了 name 属性和 gender 属性,IPerson 则使用extends 关键字多重继承的方式,继承了 IName 和 IGender,同时定义了自己的属性age,此时 IPerson 除了自己的属性外,还同时继承了 IName 和 IGender 的属性。
  ▐条件判断
  When the type on the left of the extends is assignable to the one on the right, then you’ll get the type in the first branch (the "true" branch); otherwise you’ll get the type in the latter branch (the "false" branch).
  当 extends 左边的类型可以赋值给右边的类型时,你会在第一个分支中获取获得这个类型(true),否你会在第二个个分支中获得这个类型(false)
  普通条件类型
  先来直接看个例子
  // 示例1 interface IAnimal {   name: string; }   interface IDog extends IAnimal {   color: string; }   // A的类型为string type Test = IDog extends IAnimal ? string : number;
  extends 的条件判断和 JS 中的三元表达式很类似,如果问号前面的条件为真就把 string 类型赋值给 A,否则就把 number 类型赋值给 A。那么问号前面的条件判断真假的逻辑是什么呢?就像上面的那段英文描述一样,当extends 左边的类型可以赋值给右边的类型的时候,就会真,否则为假。
  在上面的例子中,IDog 是 IAnimal 的子类,子类比父类的限制更多,如果能满足子类的条件约束,就一定能满足父类的条件约束,IDog 类型的值可以满足 IAnimal 类型,判断结果为真,Test 的类型为 string。
  再来一个例子:
  // 示例2 interface I1 {   name: string }   interface I2 {   name: string   age: number } // A的类型为string type Test = I2 extends I1 ? string : number
  这个例子,代入上面的解法来看就是,能满足 I2 类型约束的值也满足 I1 类型约束,判断结果为真,Test 的类型为 string。
  多看几个例子:
  type Test1 = "x" extends "x" ? "true" : "false";  // "true" type Test2 = "x" extends "y" ? "true" : "false"  // "false" type Test3 = 100 extends 100 ? "true" : "false"  // "true" type Test4 = 200 extends 100 ? "true" : "false"  // "false" type Test5 = {} extends {name:string} ? "true" : "false"  // "false" type Test6 = {name:string} extends {} ? "true" : "false"  // "true"
  按照上面的解释能够很好的解释出最后的结果。
  分配条件类型
  再多看几个例子:
  type Test1 = "x" extends "x" ? string : number; // string type Test2 = "x" | "y" extends "x" ? string : number; // number   type P = T extends "x" ? string : number; type Test3 = P<"x" | "y"> // tring | number   type P = "x" extends T ? string : number; type Test4 = P<"x" | "y"> // tring
  这里就先把最后的结果直接给出来了,看到 Test1、Test2 和 Test4 还能理解,但是 Test3 的结果为什么就是 string |nunber这个类型了呢?同样的按照泛型传参数,按照直觉来说,Test3 和 Test2 应该是一样的结果,为什么结果差异这么大呢?
  这里导致结果和直觉不一样的原因就是所谓的分配条件类型。
  When conditional types act on a generic type, they become distributive when given a union type
  当 extends 前面的参数是一个泛型类型,当传入的参数是一个联合类型的时候,就是使用分配律计算最后的结果,分配律就是我们从数学中学到的分配律。把联合类型中的每个类型代入条件判断得到每个类型的结果,再把每个类型的结果联合起来,得到最后的类型结果。
  那么就可以按照这个解法来代入 Test3 的解释原因:
  extends 前面的参数 T 是泛型参数,Test3 中 泛型代入的的 x | y这个联合类型,这个时候就触发了分配条件类型,来使用分配律
  "x" extends "x" ? string : number; // string "y" extends "x" ? string : number; // number type Test3 = string | number
  按照分配条件来看最后的结果恍然大悟,总之要触发分配条件类型要满足两个条件,第一,extends 前面的参数是泛型类型,第二,参数是联合类型。
  条件分配类型是系统默认的行为,那么在某些需求不想要出发条件分配类型应该怎么办呢?
  看下面的例子:
  type P = [T] extends ["x"] ? string : number; type Test = P<"x" | "y"> // number
  这个使用使用了[]这个符号把泛型类型参数包起来,这个时候 extends 前面的参数就变成这个样子["x" | "y"],不满足触发分配条件类型的条件,按照普通条件来判断,得到最后的结果为 number。
  never
  来个例子看看:
  // never是所有类型的子类型 type Test1 = never extends "x" ? string : number; // string   type P = T extends "x" ? string : number; type Test2 = P // never
  上面直接给出了最后的结果,但是为什么看起来 Test2 最后的结果又和直觉中不太一样,never 不是联合类型,直接代入条件类型之后,按理来说 Test2 和 Test1 的结果应该一样才对。
  事实上,never 被认为是空的联合类型,也就是没有任何项的联合类型,所以还是满足上面的分配条件类型,因为没有任何联合项可以分配,所以P根本就没有执行,就和永远没有返回的函数一样,属于 never 类型。
  按照上面的条件,可以这样子来阻止分配条件类型。
  type P = [T] extends ["x"] ? string : number; type Test = P // string
  ▐应用
  Exclude
  type Exclude = T extends U ? never : T;
  Exclude 是 TS 内置的应用方法,作用是从第一个联合类型参数 T 中,把第二个联合类型参数 U 出现的联合项去掉。
  type Test = Exclude<"A" | "B", "A"> // "B"
  其实就是应用了分配条件类型:
  type Test = Exclude<"A", "A"> | type Test = Exclude<"B", "A">type Test = "A" extends "A" ? never : "A" | "B" extends "A" ? never : "B" type Test = never | "B" type Test = "B"
  Extract
  type Exclude = T extends U ? T : never;
  Extract 是 TS 内置的应用方法,作用是从第二个联合类型参数 U 中,把第一个联合类型参数 T 出现的联合项提取出来。
  type Test = Exclude<"A" | "B", "A"> // "A"
  其实就是应用了分配条件类型。
  type Test = Exclude<"A", "A"> | type Test = Exclude<"B", "A"> type Test = "A" extends "A" ? "A" : never | "B" extends "A" ? "B" : never type Test = "A" | never type Test = "A"
  ▐总结
  在 typescript 中的 extends 关键字主要用法,就是继承类型,结合三元表达式来完成更多的类似函数的应用方法,在三元表达式中还要注意分配条件类型的应用。
  文章为笔者在学习过程中做的笔记,如果有误,欢迎指正。

新发现可能揭示暗物质的性质今年早些时候,一种机器学习算法检测到了多达5000个可能的引力透镜,这可能会改变科学家们描绘大爆炸以来星系演变的能力。ASTRO3D和新南威尔士大学(UNSW)的KimVyTran南极发现迄今最古老海洋DNA由澳大利亚塔斯马尼亚大学领导的一个国际研究团队在南极大陆北部斯科舍海的深海沉积物中发现了最古老的海洋DNA。这些有机物质碎片为研究气候变化对海洋生态系统的长期影响提供了宝贵线索,还长津湖一战,志愿军在彻底发现美军,不论生死均要补一枪,为何?上个世纪50年代初,朝鲜半岛南北内战爆发,美国趁机假借联合国名义,组建所谓的联合国军登陆朝鲜半岛参与战争。在美军的干预下,朝鲜半岛战火迅速蔓延,嚣张的美军司令一度放出话说圣诞节前定体检发现尿酸高了怎么办?尿酸有的人体检时发现尿酸旁边出现,就会很紧张,以为自己得了痛风。殊不知尿酸高了并非就是得了痛风。血中尿酸的浓度与体内的嘌呤代谢有关。导致尿酸升高的主要原因是排泄少了,其次是生成多了NASA称木卫二冰壳中的浅湖可能会爆发新科学研究提出了一些假设,NASA的欧罗巴快船可以测试这些假设。木卫二卫星表面的任何羽流或火山活动都是由其冰壳中的浅层湖泊引起。在我们的外太阳系中,地下水体是寻找地球以外生命的一些西藏最大的尼姑寺,700年历史传承,108座佛塔,却少有人知头条创作挑战赛行走在拉萨街头,你会发现无论何时何地,你的视线中永远不会缺少虔诚的佛教信徒和身穿红色僧衣的喇嘛。记得在拉萨工作的那段时间,每到黄昏降临的时候,就能看见三三两两的藏族老丰富的浙江瑞安自驾游目的地推荐瑞安市,地处上海经济区和厦漳泉金三角之间,经济发达,瑞安气候宜人,物资产丰富,属江南鱼米之乡。这个周末我们驱车自驾去了瑞安!一花岩森林公园花岩国家森林公园里面空气清新,含氧量高,是颜真卿故里,沂蒙山小调诞生地,内有一个世界之最头条创作挑战赛今天跟大家分享的这县,地处沂蒙山区腹地,东临兰山,西接平邑,南依兰陵,北沿蒙山自西北至东南连蒙阴沂南。县域历史文化悠久,人文底蕴深厚,春秋时称鄪(b)邑,战国时称鄪国非洲各国转机路线全面汇总今天给大家继续带来非洲转机回国航班,回家过年一周四班每周三四六日下面是航班详情阿比让回国EK788阿比让迪拜162506201EK362迪拜广州02501350阿克拉回国EK788巴彦淖尔市临河区以农文旅融合为径助力乡村振兴来源内蒙古自治区乡村振兴局巴彦淖尔市临河区注重挖掘传承中华优秀传统文化,依托边塞文化黄河文化草原文化和农耕文化等,打造乡村旅游产品和线路,推动农文旅融合发展,以农文旅融合助力乡村振练好32个大字,以备挥毫不时之需每个人难免会遇到要求拿起毛笔写两个字的机会,这个时候如果没有两个写的得意的字就尴尬了,一下32个字都是比较常用的字的草书写法,可以看到结构笔画使转,可以挑几个专门练一下,以备不时之
全智能安卓表皇!运动更专业,续航更持久OPPOWatch3Pro不得不说最近两年国产智能手表大幅度崛起,从设计到功能越来越符合我们心中智能手表的样子。智能手表越来越受到青睐的原因其实也源于大家对科技时尚的追求,一种很新的东西,把科技与时尚美学结学习Python,需要掌握的20个命令文刘海棠chocowindows系统软件安装命令chocoinstallpythonprebrewmac系统软件安装命令brewinstallpyenvaptlinux系统软件安装告新浪微博抄袭并索赔1000万的小鸡词典,停运了作为互联网冲浪的弄潮儿,自然会碰到一些不明就里的梗文化,在此过程中,手谈姬就很多次接触到小鸡词典这个解释梗的网络产品。小鸡词典由于其词典的定位,查起来要更加直观,比看视频百科来得快iPadmini7曝光终于上高刷屏了国产平板冲击中高端文名动科技近两年国产品牌接连推出平板电脑,对苹果的iPad造成了一定的打击,尤其是定位入门级的数字系列,配置方面几乎被同价位的安卓平板压着打。不过在小尺寸平板上,目前还是iPadm汉中为啥又叫兴元府头条创作挑战赛汉中曾经以皇帝年号命名,开历史之先河,这究竟是怎么一回事呢?话说自14岁经历安史之乱21岁任天下兵马元帅亲自平复安史之乱37岁登基的唐德宗李适,曾亲眼目睹政治腐败社会宋辽四十年无战事,和平是怎样达到的?公元1063年,宋仁宗赵祯驾崩,宋廷遣使告哀于契丹辽国,辽道宗耶律洪基拉着使者的手嚎啕大哭,说四十二年不识兵矣!这是出自北宋人陈师道后山谈丛的记载,随后被生活于两宋之交的邵博,在邵宰相写下青鵝两字惨遭灭门,群臣不解,武则天将字拆开念念随着现代技术的发展,我们已经很少使用传统的书写方式来写字了,因此很多人都会出现提笔忘字的情况,也不会再去探索汉字中的乐趣。其实,汉字的构成大有讲究,一个字就可以拆解出几个意思来,两岳飞墓前的秦桧跪像是何时出现的?岳飞是南宋时期抗金名将,从二十岁起先后四次从军,参与指挥大小战斗数百次,其麾下岳家军更是令金人闻风丧胆。然而国有名将,朝无正臣。奸相秦桧与昏君赵构铁了心要与金国议和,杀岳飞就成了他22岁女孩嫁给无四肢的志愿军,相爱55年,离世后丈夫为她披麻戴孝2019年,党中央把人民楷模的国家级荣誉授予朱彦夫,在此之前,朱彦夫已经荣获过全国道德模范全国自强模范全国模范伤残军人等荣誉。2021年,朱彦夫又荣获了感动中国2021年度人物。这1940年,军统女特工冒死救下一位地下党,此举11年后反救自己一命1940年5月的一天傍晚,军统西南站办公地点,一个军统女特工趁人不备,突然悄悄咬破自己的舌头,边吐血沫边念叨我头晕。一众同事见了,急忙将她送往医院,安排医生进行诊疗。同事们散去后,宋代钱币为什么被称为币中骄子?在我国悠久的货币历史中,诞生出丰富的钱币类型,然而却有这么一种货币被堪称币中骄子。它就是宋代钱币。宋代钱币公元959年宋太祖赵匡胤通过陈桥兵变,黄袍加身后建立了宋朝,此后宋朝文化艺