TypeScript 中的 泛型

本文最后更新于:2022年4月22日 上午

TypeScript  中的   泛型

泛型的基本使用

泛型是  TypeScript  中较为强大的一种类型

泛型可以将  类型参数化 ,将原来预先写好的类型变成由使用者使用的时候作为参数传入

泛型的基本语法为 <> ,中间为泛型的形参

function reflect<P>(param: P) {
  console.log(param);
}

这里可以看到,<> 中间为泛型的定义,param的类型表示为泛型P,返回值也为泛型 P

当我们调用这个方法的时候,在方法名的后面加上一个 <> 将此次的具体类型传入进去

reflect<number>(123); // 类型为:function reflect<number>(param: number): void
reflect<string>('123'); // 类型为:function reflect<string>(param: string): void

当然,也可以不显式传入泛型的类型,TypeScript 会帮助我们做类型推导

reflect(123); // 类型为:function reflect<number>(param: number): void
reflect('123'); // 类型为:function reflect<string>(param: string): void

泛型约束数组、对象

泛型还可以用来约束数组,对象等类型

function reflect2<P>(param: P[]) {
  console.log(param);
}

reflect2([1, 2, 3]); // 类型为:function reflect2<number>(param: number[]): void
reflect2([1, 2, '3']); // 类型为:function reflect2<string | number>(param: (string | number)[]): void

多个泛型参数

多个泛型形参,多个泛型可以用逗号隔开

function reflect3<P, T>(param: P, type: T): [P, T] {
  return [param, type];
}

reflect3(123, 'abc');
// 类型为 function reflect3<number, string>(param: number, type: string): [number, string]

泛型 的接口使用

除了可以在函数上使用泛型,我们还可以在接口中使用泛型

interface IInfo<N, A> {
  name: N;
  age: A;
}

const info: IInfo<string, number> = {
  name: '小明',
  age: 18,
};

泛型 在类中的使用

泛型在类中也是可以使用

class Person<N> {
  name: N;
  constructor(name: N) {
    this.name = name;
  }

  getName(): N {
    return this.name;
  }
}

// const p = new Person<string>('小明');
const p = new Person('小明'); // 也可以类型推导

泛型的默认值

可以看到,在接口类型中,我们需要显式的给泛型指定类型,当属性多的话,这种方式会较为低效,那有没有什么方式可以帮助我们设置默认值呢,答案是有的

我们可以在声明泛型的时候, 在  ** =  号的右边设置泛型的默认值**

interface IInfo<N = string, A = number> {
  name: N;
  age: A;
}

const info: IInfo = {
  name: '小明',
  age: 18,
};

泛型约束

我们在使用泛型的时候,可以给泛型添加一些约束,使调用者调用的时候必须符合这些约束

在声明泛型的时候   后面跟上  extends  关键字,表示这个泛型的参数需要符合   这个类型

基本约束

function reflect<P extends string | number>(param: P) {
  console.log(param);
}
reflect('123');
reflect(123);
// reflect(true); // 类型“boolean”的参数不能赋给类型“string | number”的参数。ts(2345)

进阶约束

function reflect2<P extends { name: string; age: number }>(param: P) {
  console.log(param);
}
reflect2({ name: '小明', age: 18 });
reflect2({ name: '小红', age: 18, like: '唱歌' });
// reflect2({ name: '小东' });   //  缺少属性 "age"

interface IPerson {
  name: string;
}

function reflect3<P extends IPerson>(param: P) {
  console.log(param);
}

const p: IPerson = { name: '小明' };

reflect3(p);
reflect3({ name: '小红', age: 18 });
// reflect3(123);  // 类型“number”的参数不能赋给类型“IPerson”的参数。ts(2345)

泛型间互相约束

function setVal<O extends {}, K extends keyof O, V extends O[K]>(obj: O, key: K, val: V) {
  obj[key] = val;
}

const info = {
  name: '小明',
  age: 18,
};


// setVal(info,);
/*
当我们传入第一个对象的时候,IDE 自动给了我们提示
setVal(obj: { name: string; age: number; }, key: "name" | "age", val: string | number): void
*/
setVal(info, 'name', '小红');
setVal(info, 'age', 19);
// setVal(info, 'name', true); // 类型“boolean”的参数不能赋给类型“string”的参数。ts(2345)

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议,转载请注明出处。