欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

深入理解 TypeScript 的泛型机制

最编程 2024-07-27 22:02:35
...

泛型可以为封装的代码提升可重用性,不仅支持当前的数据类型,同时也能支持未来的数据类型。

泛型函数的基本使用

举个例子:创建一个函数,功能是返回值的类型与传入的参数类型相同。

下面是使泛型与不使用泛型的对比:

//不使用泛型,这样虽然也是可以满足需求,但是会丢失一些信息,比如传入一个数字,我们只能知道任何类型都是可能被返回的。
function getSameType(x:any):any{
  return x;
}
//使用泛型,T 是类型变量,它可以帮助我们捕获输入的类型,之后再返回 T,可以确保传入的类型与返回类型是相同的。
function getSameType <T>(x:T):T{
  return x;
}
//泛型的使用方式有两种,第一种是直接传入 T 的类型,第二种是利用编译器的类型推断自动帮我们确定 T 的类型
getSameType<string>("直接传入 T 的类型");
//利用编译器类型推论,某些情况可能无法推断出正确的类型,需要使用第一种方式,在一些复杂的情况下,这是可能出现的
getSameType("利用类型推断自动确定 T 的类型");

使用泛型变量

当我们使用泛型变量时,编译器要求你必须把参数当作 any 或者 所有类型来使用,否则会报错,如下所示:

function getSameType <T>(x:T):T{
  //因为你可以能传入的是一个 number 类型,number 是没有.length 的
console.log(x.length); // Error: T doesn't have .length
  return x;
}

如果需要使上面的例子不报错的话,那么需要把 T 当作 T 类型的数组即可

function getSameType <T>(x:T[]):T[]{
  console.log(x.length); // 由于数组有 length 属性所以不会报错
  return x;
}

泛型类型

泛型函数的类型和非泛型函数的类型没什么不同,只是一个类型参数在最前面****(arg: U) => U,像函数声明一样

function identity<T>(arg: T): T {
    return arg;
}
//可以使用不同的泛型参数名,只要数量上和使用方式一致即可。
let myIdentity: <U>(arg: U) => U = identity;

泛型接口类型,只需要在接口名后加泛型即可。

interface interFan<T>{
  (arg:T):T;
}
let obj:interFan<string> = function(x:T):T{
  return x;
}

泛型类与泛型接口一致,只需要在类名称后加即可。但是泛型变量只属于类的实例部分,类有两个部分:静态部分和实例部分,静态部分如 construtor 构造函数无法使用泛型。

class GenericNumber<T> {
    zeroValue: T;
    add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };

泛型约束

之前我们说过,泛型变量 T,编译器要求我们必须把它当作 any 或者任意类型,但是为了我们使用具体的属性如 length,我们必须要保证至少有这个属性的话,我们需要使用泛型约束。

//泛型约束,让 T 继承接口,保证 T 必须符合接口 fanInter 来实现约束
interface fanInter{
  length:number
}
function fanInter<T extends fanInter>(arg:T):T{
  console.log(arg.length);
  return arg;
}
//你们可能会好奇为什么这样可以通过接口的严格检测,因为我们多了个接口未定义的属性 value,泛型约束与接口的赋值声明不同,泛型约束是属于宽松型检测,只要有规定的属性即可,因为泛型约束的意义是为了使用某些属性时不会报错,并且可以得到一些信息。
fanInter({length: 10, value: 3});

泛型约束中使用类型参数

如果我们需要从一个对象中获取一个属性,并且确保属性就在这个对象上,那么就需要在两个类型之间进行约束

function getProperty(obj: T, key: K) {
    return obj[key];
}
let x = { a: 1, b: 2, c: 3, d: 4 };
getProperty(x, "a"); // okay
getProperty(x, "m"); // error: 必须是 x对象中的属性!

在泛型中使用类类型

//在泛型中使用类类型
class Animal {
    numLegsnumber;
}
class Bee extends Animal {}
class Lion extends Animal {}
//泛型约束为 Animal 并且返回 Animal类型
function createInstance<A extends Animal>(cnew () => A): A {
    let obj = new c();
    console.log(obj.numLegs)
    return obj ;
}
createInstance(Lion).numLegs  // success!
createInstance(Bee).numLegs   // success!