TS:泛型
1. 前言
-
有时候,我们想让一个函数的参数和返回类型是相同的,就可以使用类型变量。
-
类型变量是一种特殊的变量,用于表示类型而不是值。
function identity<T>(arg : T) : T{ return arg; }
-
定义了泛型函数后,可以用两种方法使用。
-
一种是传入所有的参数,包括类型参数:
let out = identity<string>("yivi");
-
另一种是使用类型推论——即编译器会自动推断类型:
let out = identity("yivi");
-
类型推论帮助我们保持代码精简和高可读性。
2. 泛型变量
-
来看看一个例子:
function foo<T>(arg : T): T{ console.log(arg.length); // error,arg的类型为T,无明确指示方法,因此报错。 return arg; }
-
当我们想操作T类型的数组时,
.length
的属性是存在的,因此不会报错:function foo<T>(args : T[]):T[]{ console.log(args.length); return args; } // or function foo<T>(args : Array<T>) : Array<T>{ console.log(args.length); return args; }
3. 泛型类型
-
我们可以使用不同的泛型参数名,只要保证数量和使用方式一致即可;
-
也可以使用带有调用签名的对象字面量来定义泛型函数;
function identity<T>(arg : T) : T{ return arg; } let myIdentity : <U>(arg : U) => U = identity; // or let myIdentity : { <T>(arg : T):T} = identity;
-
将上面的对象字面量拿出来写成一个接口:
interface IdentityInterface{ <T>(arg : T) : T; } function identity<T> (arg : T) : T{ return arg; } let myidentity : IdentityInterface = identity;
-
也可以将泛型参数当作整个接口的一个参数,这样就保证我们知道使用的是哪个泛型类型:
interface IdentityInterface<T>{ (arg : T) : T; } function identity<T> (arg : T) : T{ return arg; } let myidentity : IdentityInterface<string> = identity;
-
泛型类与泛型接口差不多,都使用<>来进行约束:
class Foo<T>{ state : T; add : (x : T, y : T) => T; } let myfoo = new Foo<number>(); myfoo.state = 1; myfoo.add = (x,y)=>{ return x + y; }
4. 泛型约束
-
上面的例子中,我们提到了
.length
这个属性如果用在泛型上,就会报错; -
因此,我们只需要实现一个接口,让该泛型有这个属性,就可以防止编译器报错了;
interface Length{ length : number; } function identity<T extends Length>(arg : T) : T{ console.log(arg.length); return arg; }
-
虽然编译器不会报错了,但如果使用了没有该属性的类型,也是同样会报错;
-
所以我们需要传入符合约束的值,且包含必须的属性;
identity({ length : 10,value : 100});
5. 在泛型中使用 类 类型
-
在typescript中使用泛型创建工厂函数时,需要引用构造函数的类类型。
function create<T> (c : { new() : T}) : T{ return new c(); } class Foo{ name : string; } let a : Foo = create(Foo); a.name = 'yivi' console.log(a.name)