TS文档学习 --- Generics

128 阅读2分钟

本篇整理自 TypeScript Handbook 中 「Generics」 章节。

泛型- 带参数的类型

Generics 初探

类型变量是一种用在类型而非值上的特殊的变量。是一种可以捕获参数类型的方式,然后再用它表示返回值的类型

// 定义
function identity<Type>(arg: Type): Type {
  return arg;
}

// 使用
// 方式1 --- 是传入所有的参数,包括类型参数 === 类型参数定义在<>中
let output = identity<string>("myString")

// 方式2 --- 类型参数推断 --- 常见
let output = identity("myString");

泛型函数的形式就跟其他非泛型函数的一样,都需要先列一个类型参数列表

function identity<Type>(arg: Type): Type {
  return arg;
}

// 函数表达式
let myIdentity: <Type>(arg: Type) => Type = identity;

// 调用签名
let myIdentity: { <Type>(arg: Type): Type } = identity;
// 使用调用签名定义函数接口
interface GenericIdentityFn {
  <Type>(arg: Type): Type;
}
 
function identity<Type>(arg: Type): Type {
  return arg;
}
 
let myIdentity: GenericIdentityFn = identity;

注意,TypeScript中不存在泛型枚举类型和泛型命名空间

泛型类

泛型类写法上类似于泛型接口。在类名后面,使用尖括号中 <> 包裹住类型参数列表

class GenericNumber<NumType> {
  zeroValue: NumType;
  add: (x: NumType, y: NumType) => NumType;
}

一个类它的类型有两部分:静态部分和实例部分。泛型类仅仅对实例部分生效

所以当我们使用类的时候,注意静态成员并不能使用类型参数

class user<NumType> {
  username: NumType // success
  static username: NumType; // error

  constructor(username: NumType) {
      this.username = username
  }
}

泛型约束

可以在泛型中使用这个接口和 extend关键词实现约束

interface Lengthwise {
  length: number;
}
 
function loggingIdentity<Type extends Lengthwise>(arg: Type): Type {
  console.log(arg.length); // Now we know it has a .length property, so no more error
  return arg;
}

在泛型约束中使用类型参数

可以声明一个类型参数,这个类型参数被其他类型参数所约束

function getProperty<Type, Key extends keyof Type>(obj: Type, key: Key) {
  return obj[key];
}
 
let x = { a: 1, b: 2, c: 3, d: 4 };
 
getProperty(x, "a"); // success
getProperty(x, "m"); // error

在泛型中使用类类型

当使用工厂模式创建实例的时候,有必要通过他们的构造函数推断出类的类型

function create<Type>(c: { new (): Type }): Type {
  return new c();
}

下面是一个更复杂的例子,使用原型属性推断和约束,构造函数和类实例的关系

class BeeKeeper {
  hasMask: boolean = true;
}
 
class ZooKeeper {
  nametag: string = "Mikle";
}
 
class Animal {
  numLegs: number = 4;
}
 
class Bee extends Animal {
  keeper: BeeKeeper = new BeeKeeper();
}
 
class Lion extends Animal {
  keeper: ZooKeeper = new ZooKeeper();
}
 
function createInstance<A extends Animal>(c: new () => A): A {
  return new c();
}
 
createInstance(Lion).keeper.nametag;
createInstance(Bee).keeper.hasMask;