TypeScript 类、泛型的使用实践记录 | 青训营

79 阅读3分钟

一、什么是泛型

在开发过程中,我们不仅要创建定义一个好的类,接口,方法等,同时要考虑重用性,不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能。

可以提供泛型约束的成员:

  • 类的实例成员
  • 类的方法
  • 函数参数
  • 函数返回值

eg:

平时我们定义与一个函数

function getValue(value:Number) :Number{
    return value
}

这里我们将Number类型分配给参数和返回类型,使该函数仅可用于该原始类型。但该函数并不是可扩展或通用的,很明显这并不是我们所希望的。

那么引入泛型以后,我们可以解决只能返回特定类型的问题

function getValue<T>(value:T) :T{
    return value
}
getValue<string>('hello')  //'hello'
getValue<Number>(123)     //123

注意:除了为类型变量显式设定值之外,一种更常见的做法是使编译器自动选择这些类型,从而使代码更简洁。我们可以完全省略尖括号

二、使用方法与场景

泛型接口

interface Identities<V, M> {
  value: V,
  message: M
}

在上述的 Identities 接口中,我们引入了类型变量 VM,来进一步说明有效的字母都可以用于表示类型变量,之后我们就可以将 Identities 接口作为 identity 函数的返回类型:

function identity<T, U> (value: T, message: U): Identities<T, U> {
  console.log(value + ": " + typeof (value));
  console.log(message + ": " + typeof (message));
  let identities: Identities<T, U> = {
    value,
    message
  };
  return identities;
}
​
console.log(identity(68, "Semlinker"));

泛型类

在类中使用泛型也很简单,我们只需要在类名后面,使用 <T, ...> 的语法定义任意多个类型变量

class myClass<T>{
    value:T
    constructor(value:T){
        this.value=value
    }
    getValue():T{
        return this.value
    }
}
const myNumber=new myClass<Number>(1)
myNumber.getValue()  //1
const myString=new myClass<string>('hello')
myString.getValue()   //hello

泛型约束与安全

有时我们可能希望限制每个类型变量接受的类型数量,这就是泛型约束的作用。可以确保属性是否存在,检查对象上的键是否存在。

确保属性存在

有时候,我们希望类型变量对应的类型上存在某些属性。这时,除非我们显式地将特定属性定义为类型变量,否则编译器不会知道它们的存在。

function identity<T>(arg: T): T {
  console.log(arg.length); // Error
  return arg;
}

在这种情况下,编译器将不会知道 T 确实含有 length 属性,所以会报错

我们可以使用extends一个接口

interface Length {
  length: number;
}function identity<T extends Length>(arg: T): T {
  console.log(arg.length); // 可以获取length属性
  return arg;
}

T extends Length 用于告诉编译器,我们支持已经实现 Length 接口的任何类型。之后,当我们使用不含有 length 属性的对象作为参数调用 identity 函数时,TypeScript 会提示相关的错误信息

检查对象上的键是否存在

泛型约束的另一个常见的使用场景就是检查对象上的键是否存在.

keyof 操作符是在 TypeScript 2.1 版本引入的,该操作符可以用于获取某种类型的所有键,其返回类型是联合类型。

interface Person {
  name: string;
  age: number;
  location: string;
}
​
type K1 = keyof Person; // "name" | "age" | "location"
type K2 = keyof Person[];  // number | "length" | "push" | "concat" | ...
type K3 = keyof { [x: string]: Person };  // string | number

通过 keyof 操作符,我们就可以获取指定类型的所有键,之后我们就可以结合前面介绍的 extends 约束,即限制输入的属性名包含在 keyof 返回的联合类型中。具体的使用方式如下:

function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

总结

泛型是typescript比较常用的特性,其他语言中都存在泛型的概念,使用泛型解决了重复代码的问题,使用泛型约束,确保了代码的安全性和灵活性。