当前位置: 首页 > 科技观察

[前端]大家好,我叫TypeScript03──数据类型

时间:2023-03-12 22:56:44 科技观察

写在泛型前面。今天是520,祝有情人终成眷属。让我们也因应形势,解锁两个新英雄:弹跳和跳跃跳入,探索全新的问答视角,引入并解决燃眉之急。什么是泛型?《蹦蹦》:跳跃,你知道什么是泛型吗?《跳跃》:历史上,强者和高手总是考虑现在和未来。只有宏观把握,才能站稳不败之地。就像你玩游戏的时候,不管你要出什么大件装备,都可以先买一对孩子,多兰剑,蓝戒指,这样就不会出错了。无论如何,你需要在这方面进行扩展。在面向对象的编程语言中,组件不仅要考虑当前的数据类型,还要考虑将来要使用的数据类型,而“泛型”完美地提供了组件的“可重用性”,提高了灵活性。你知道吗?为什么要设计泛型?《蹦蹦》:跳转,为什么要设计泛型?“跳转”:设计泛型的目的是在成员之间提供有意义的约束,而这些成员包括:类实例成员、类方法、函数参数和函数返回值。《跳》:JS哥作为动态语言存在,具有极大的灵活性。只有在执行过程中给变量赋值,才能知道变量是什么类型。那么就有一个隐患。事先不知道赋值什么类型的变量,所以在执行的时候会出现类型错误的错误,降低了代码的可维护性。TS中有3个方法可以解决JS的复用性和可维护性差的问题,我们来看看:函数重载functiongetVal(val:number):numberfunctiongetVal(val:string):stringfunctiongetVal(val:any):any{returnval;}联合类型函数getVal(val:string|number|any[]):string|number|any[]{returnval;}在追求代码简洁易读的时代,你写这臭臭的长代码有几个意思,有点乱。而TS小哥还是会来的,提前考虑这些问题,提供解决这些问题的通用方法。泛型函数getVal(val:T):T{returnval;}解释了泛型类型的规则。上面的“T”表示传入要捕获的函数的参数类型(Type)。在函数内部使用T可用于此参数类型声明其他变量。实际上T不是固定的,可以用任何有效名称替换。例如:K(Key):表示对象中的键类型V(Value):表示对象中的值类型E(Element):表示元素的类型如何使用泛型??《跳》:发呆用仿制药,系好安全带,我们出发了。我们看到调用identity(1)时,Number类型就像参数1一样,凡是出现T的地方都会填那个类型。图中内部的T称为类型变量,是我们要传递给identity函数的类型占位符,赋值给value参数来代替它的类型:此时T作为一个类型,而不是一个具体的数字类型。《蹦蹦》:写的挺详细的,那个<>里面可以写两个变量类型吗?《跳》:这个肯定绰绰有余,就算你有两个,两百个也绰绰有余了。让我举一个例子。我们可以看到在下面的代码中,使用了两个类型变量来扩展我们定义的恒等函数:除了显式地为类型变量设置值外,一种更常见的做法是让编译器自动选择这些类型,这使代码更简洁。我们可以完全省略尖括号,使用类型推断——即编译器会根据传入的参数自动帮我们判断T的类。类型推断帮助我们保持代码的紧凑性和可读性。letoutput=identity("myString");//typeofoutputwillbe'string'letoutput=identity("myString");//typeofoutputwillbe'string'泛型接口和泛型类“蹦蹦”:跳,我昨天看了《一川》写的关于接口的文章,看到有对象接口,类接口等等,泛型是否也有相关的概念。“跳跳”:不错,很快就会有答案的。先来看看泛型接口:interfaceFanxingInter{//Definesanon-genericfunctionsignatureaspartofthegenerictype(name:T):T;}functionfunc(name:T):T{returnname;}//使用泛型接口时,需要传入一个类型参数来指定泛型类型(这里是数字),锁定后锁定代码中使用的类型letfxFun:FanxingInter=func;fxFun("宜川");下面我们来看看泛型在类中是如何使用的,以及泛型类的定义。事实上,泛型类看起来像一个泛型接口。泛型类使用(<>)括起泛型类型,后跟类名,以定义任意数量的类型变量。interfaceFxInterface{value:T;getValue:()=>T;}classFxClassimplementsFxInterface{value:T;constructor(value:T){this.value=value;}getValue():T{returnthis.value;}}constmyFxClass=newFxClass("yichuan");console.log(myFxClass.getValue());调用过程:首先实例化对象FxClass,传入字符串类型参数值"yichuan";在FxClass类中,类型变量T的值变成了字符串类型;FxClass类实现了FxInterface,此时T表示字符串类型,即实现了泛型接口;我们总结为:functionFunc(){}//泛型函数,尖括号在函数名后面classDog{}//泛型类,尖括号在类名后面interfaceNiuInterface{}//Genericinterface,尖括号跟在接口名后面“跳转”之后:bouncing,泛型接口和泛型类,你明白了吗?通用约束“弹跳”:明白了。突然想到一个问题,如果要操作某个类型对应的某些属性,比如访问参数名的长度,为什么编译器会报错?functionnameFunc(name:T):T{console.log(name.length);//Errorreturnname;}《跳转》:这道题挺好的,说明你泛型很厉害。我们看到,因为编译器不知道你输入的参数类型T是否有length属性,为了解决这个问题,可以让类型变量扩展包含需要的属性的接口。interfaceLength{length:number;}functionidenLength(name:T):T{console.log(name.length);returnname;}TextendsLength是对泛型的约束,告诉编译器支持任何Length接口类型。当以没有length属性的对象作为参数调用函数时,ts会提示相应的错误信息:console.log(idenLength(18));//Error此外,我们还可以使用,来分隔多个约束类型,例如:。至于上面的length属性问题,如果我们显式设置变量为数组类型,也可以解决问题。高手──索引类型“轰”:泛型如何判断一个对象的key是否存在?《轰隆隆》:其实很简单,也是用泛型约束来做检测。它只需要使用索引类型查询操作符:keyof,用于获取某个类型T的所有键,并返回该类型的公共属性的联合类型。interfacePerson{name:string;age:number;}letpersonProps:keyofPerson;//"name"|"age"我们可以结合前面介绍的extends约束,即限制输入属性name包含在返回的联合类型中键。functiongetProperty(obj:T,key:K):T[K]{returnobj[key];}在上面的getProperty函数中,我们使用KextendskeyofT来保证参数key必须包含在对象键,以便不会发生运行时错误。classBeeKeeper{hasMask:boolean;}classZooKeeper{nametag:string;}classAnimal{numLegs:number;}classBeeextendsAnimal{keeper:BeeKeeper;}classLionextendsAnimal{keeper:ZooKeeper;}functioncreateInstance(c:new()=>A):A{returnnewc();}createInstance(Lion).keeper.nametag;//类型检查!createInstance(Bee).keeper.hasMask;//类型检查!《蹦蹦》:懂了懂了,就是有点复杂,只好去捏捏了。总结“跳槽”:其实本文主要介绍:泛型的概念、泛型接口和泛型类、泛型约束和索引类型等。参考文章《重学TS》《ts中文文档》by阿宝哥本文转载自微信公众号《前端引力》,可通过以下二维码关注。转载本文请联系前端Gravity账号。