大家好,这里是鸽子餐厅,帮大家吃饭,吃不饱就被赶回家。泛型的定义和作用泛型是TypeScript中的一种高级类型,它允许你在定义函数、类和接口时将类型作为参数传入,而不是指定具体的类型,从而实现更通用的代码。学习泛型时,需要了解以下概念:泛型类型参数(typeparameter):用于指定泛型类型的占位符。泛型函数:采用泛型类型参数的函数。泛型类:采用泛型类型参数的类。泛型约束:用于约束泛型类型参数,保证泛型类型参数的特定属性或方法的存在。下面是一个简单的泛型函数的例子:functionidentity(arg:T):T{returnarg;}在这个函数中,表示这是一个泛型函数,T是一个泛型类型参数。该函数接受一个类型为T的参数arg,并原封不动地返回它。泛型函数的调用方式与普通函数类似,区别在于调用时需要指定具体的类型参数:constresult1=identity("hello");constresult2=identity(123);在这个例子中,我们单独调用恒等函数,并指定具体的类型参数。第一次调用时,类型参数是string,返回值类型也是string;第二次调用时,类型参数是number,返回值类型也是number。通过这个例子,你可以初步了解泛型的基本用法,包括泛型函数的定义和调用。接下来,您需要更深入地研究泛型的用法和约束。泛型函数使用泛型函数时,需要在函数名后使用尖括号(<>)来指定类型参数。例如:functionidentity(arg:T):T{returnarg;}letoutput=identity("helloworld");本例中identity是一个泛型函数,参数类型为,传入参数和返回值均为T类型。调用identity函数时指定参数类型为string,函数返回值为"helloworld”。通用接口定义通用接口通用接口是可以指定参数类型的接口。定义泛型接口的语法如下:interfaceinterfaceName{propertyName:T;}其中,代表一个类型参数,类型参数的名称可以根据需要自定义。泛型函数可以包含多个类型参数,每个参数用逗号分隔。使用泛型接口使用泛型接口时,需要在接口名称后用尖括号(<>)指定类型参数。例如:interfaceGenericIdentityFn{(arg:T):T;}functionidentity(arg:T):T{returnarg;}letmyIdentity:GenericIdentityFn=identity;本例中,GenericIdentityFn是一个类型参数为的泛型接口,它定义了一个函数,接受一个T类型的参数,返回一个T类型的函数。调用myIdentity函数时,指定类型参数为number,函数返回值是一个number类型的值。泛型类定义泛型类泛型类是具有一个或多个类型参数的类。定义泛型类的语法如下:classClassName{propertyName:T;}其中表示类型参数,类型参数的名称可以根据需要自定义。使用通用类使用通用类类似于使用通用函数和通用接口。首先,我们需要在实例化泛型类时指定类型参数。例如,如果我们有一个泛型类MyClass,我们可以这样使用它://definegenericclassclassMyClass{value:T;构造函数(值:T){this.value=value;}}//实例化泛型类constmyInstance=newMyClass("Hello,world!");//访问泛型类的属性console.log(myInstance.value);//输出:你好,世界!在这个例子中,我们定义了一个泛型类MyClass并使用T作为类型参数。该类有一个类型为T的属性值。我们可以在构造函数中用值初始化该属性。在实例化一个泛型类时,我们必须为T指定一个类型参数。在这个例子中,我们实例化一个MyClass的实例,并传入一个类型为字符串“Hello,world!”的参数。最后,我们输出“Hello,world!”通过访问实例的值属性。如果我们要使用非常通用的类型参数,例如any,那么我们可以省略类型参数规范,让TypeScript推断类型。例如//实例化一个泛型类并省略类型参数constmyInstance=newMyClass("Hello,world!");//访问泛型类的属性console.log(myInstance.value);//output:Hello,world!在这个例子中,我们实例化了一个MyClass的实例,并传入了一个字符串类型的参数“Hello,world!”。由于我们没有指定类型参数,TypeScript推断T的类型是字符串。泛型约束泛型约束用于限制传入的类型参数必须满足某些条件才能在函数或类中使用。泛型约束可以通过以下两种方式实现:通过继承接口实现泛型约束使用keyof关键字约束泛型类型参数下面分别介绍两种方式。通过继承接口实现泛型约束通过继承接口实现泛型约束可以保证传入的类型参数满足接口定义的要求。interfaceLengthwise{length:number;}functionloggingIdentity(arg:T):T{console.log(arg.length);//现在我们知道它有一个.length属性,所以不再有错误returnarg;}loggingIdentity("hello");//Output:5在上面的例子中,loggingIdentity函数接受一个泛型参数T,并对T进行约束,使其必须满足Lengthwise接口定义的要求,即具有length属性。这样我们就可以直接在函数中使用传入参数的length属性,不需要进行类型判断。使用keyof关键字约束泛型类型参数使用keyof关键字约束泛型类型参数,可以限制传入的类型参数为指定类型的属性之一。functiongetProperty(obj:T,key:K){returnobj[key];}letx={a:1,b:2,c:3,d:4};getProperty(x,"a");//OKgetProperty(x,"e");//Error上面的例子中,getProperty函数接受两个参数,一个是对象obj,一个是属性名key。通过使用keyofT,可以限制传入的类型参数K为对象T的属性之一。这样可以确保obj[key]的类型是有效的。需要注意的是,使用keyof关键字时,属性名必须是已知类型。因此,如果要使用变量作为属性名,可以使用索引类型查询运算符[]。functiongetProperty(obj:T,key:K){returnobj[key];}letx={a:1,b:2,c:3,d:4};letkey="a";getProperty(x,键);//ErrorgetProperty(x,keyaskeyoftypeofx);//OK在上面的例子中,变量key的类型是string,不能直接作为getProperty的第二个函数传入参数,可以使用keyoftypeofx将key转换为类型"a"|“乙”|“c”|“d”,它是对象x的属性之一。我能看到这一点,恭喜你,你已经超越了70%的人。泛型默认值在TypeScript中,我们可以为泛型类型参数指定默认值,在使用泛型时不显式传入。值,将使用指定的默认值。语法如下:functionfunctionName(args:T):void{//functionbody}其中表示泛型类型参数T有默认值defaultValue。例如,让我们定义一个返回数组最后一个元素的函数,如果数组为空,则返回默认值-1。函数last(arr:T[]):T|数{返回arr.length>0?arr[arr.length-1]:-1;}console.log(last([1,2,3]));//输出3console.log(last([]));//输出-1console.log(last([]));//输出-1在上面的代码中,我们使用泛型类型参数T来表示数组的元素类型。如果没有传递泛型类型参数,则默认为数字。在函数实现中,我们使用了默认值-1,当数组为空时返回。请注意,这里我们传递了一个空数组,因为我们没有传递泛型类型参数,所以它默认为数字类型。所以即使我们传了一个空字符串数组,最终的返回值依然是-1。当然,如果我们显式指定了泛型类型参数,比如传递一个string类型的数组,最终的返回值还是-1,因为我们使用的是默认的泛型类型参数个数。泛型默认值的定义泛型默认值是指当泛型类型没有显式传入具体类型时,可以为泛型类型设置一个默认值,从而可以使用默认值代替具体类型。定义泛型时,使用=号设置泛型的默认值,如下所示:泛型类型T的值设置为字符串,当没有显式传入特定类型时,T将默认为字符串类型。通用默认值的使用使用通用默认值允许您在不传递具体类型的情况下使用泛型。函数标识(arg:T):T{returnarg;}constresult1=identity("hello");//result1的类型为stringconstresult2=identity(123);//result2的类型为numberconstresult3=identity();//result3类型为string,因为T默认为string类型上面代码中调用identity函数时,第一个参数传入字符串,第二个参数传入数字,第三个参数不传在特定类型。由于泛型T的默认值设置为string,所以第三个参数的类型为string。看到这里,恭喜大哥,你已经超越了90%的人。泛型类型推断的概念类型推断是指TypeScript在没有明确指定类型的情况下,根据变量的使用上下文自动推断出变量的类型。.这个过程发生在编译期间。泛型类型推断规则调用函数时,如果没有显式指定泛型类型参数,TypeScript会根据传入的参数自动推断出泛型类型参数的类型。函数标识(arg:T):T{returnarg;}constresult=identity("hello");//resulttypeisstring上面代码中,调用identity函数时,传入参数“hello”,TypeScript会自动推导出T类型为string。此外,TypeScript支持从函数返回值推断泛型类型参数的类型。functioncreateArray(length:number,value:T):T[]{returnArray.from({length},()=>value);}constresult=createArray(3,"hello");}//结果类型为string[]上面代码中,调用createArray函数时,传入参数3和"hello",TypeScript会自动推导出T类型为string,所以结果类型为细绳[]。泛型在函数中的应用在TypeScript中,我们可以使用泛型来创建可重用的函数。通过将类型参数传递给函数,我们可以确保该函数适用于多种不同的类型,而不仅仅是一种类型。泛型函数定义如下:functionidentity(arg:T):T{returnarg;}在此示例中,表示类型参数。这意味着恒等函数采用T类型的参数并返回T类型的值。在调用泛型函数时,我们可以传递不同类型的参数:letoutput1=identity("hello");让output2=identity(42);在这个例子中,我们将string和number作为类型参数传递给identity函数,它们都工作得很好。泛型在类中的应用和函数一样,我们可以在TypeScript中使用泛型来创建可重用的类。通过将类型参数传递给一个类,我们可以确保该类适用于许多不同的类型,而不仅仅是一种类型。泛型类定义如下:classGenericClass{private_value:T;构造函数(值:T){this._value=value;}getValue():T{returnthis._value;}}在此示例中,表示类型参数。这意味着GenericClass类接受一个类型T的参数,并有一个名为_value的私有属性和一个名为getValue的公共方法,该方法返回一个类型T的值。调用泛型类时,我们可以传递不同类型的参数:letstringClass=newGenericClass<字符串>("你好");让numberClass=newGenericClass(42);在这个例子中,我们将字符串和数字作为类型参数传递给GenericClass类,它们都可以正常工作。泛型在接口中的应用与函数和类一样,我们可以在TypeScript中使用泛型来创建可重用的接口。通过将类型参数传递给接口,我们可以确保该接口适用于许多不同的类型,而不仅仅是一种类型。通用接口定义如下:interfaceGenericInterface{value:T;getValue():T;}在此示例中,表示类型参数。这意味着GenericInterface接口有一个名为value的T类型的属性和一个名为getValue的方法,该方法返回一个T类型的值。在调用泛型接口时,我们可以传递不同类型的参数:letstringInterface:GenericInterface={value:"hello",getValue(){returnthis.value;},};让numberInterface能看到这个,恭喜你大哥,你已经超越了98%的人。泛型的局限性尽管泛型非常灵活,但仍然存在一些局限性。以下是泛型的一些常见限制:不能使用原始类型作为类型参数。例如,不能声明其元素为数字或字符串类型的数组类型Array。如果您尝试这样做,您将得到一个编译器错误。无法访问类型参数的属性或方法。由于类型参数可以是任何类型,因此编译器无法推断类型参数具有哪些属性或方法。因此,在使用泛型时,只能访问类型参数的公共成员。不能使用运算符。由于泛型是在运行时确定的,因此+或-等运算符不能用于泛型类型。如何改进泛型的使用以下是一些可以改进泛型使用的技巧:使用泛型约束。泛型约束可以限制类型参数的类型范围,从而可以在类型参数上使用属性和方法。使用默认类型参数。默认类型参数允许在调用泛型时省略类型参数,并使用默认值代替。使用泛型类型推断。泛型类型推断允许编译器自动推断类型参数的类型。编写通用代码。尝试编写可重用的通用代码,以便在需要时可以轻松重用。