Skip to content

TS 泛型

作者:guo-zi-xin
更新于:9 个月前
字数统计:1.4k 字
阅读时长:5 分钟

定义

在 TypeScript 中,泛型(Generics)是一种强大的类型工具,它允许我们编写可重用、灵活和类型安全的代码。泛型允许我们在定义函数、类或接口时使用类型参数,这些类型参数在使用时可以被动态地指定具体的类型。

特点:

不是事先定义好类型,而是在使用的时候再指定具体类型

1. 数组泛型(Array Generic)

我们在指定一个数组的类型时,可以使用 类型 + 中括号 表示法来定义,例如: number[]来作为数字数组类型,字符串数组为string[], 除此之外,我们也可以使用泛型Array<elemType>来定义一个数组:

typescript
const arr: Array<number> = [1,2,3,4,5]
const strArr: Array<string> = ['a','b','c']
const anyArr: Array<any> = [1, 'b', [3,4], {text: 'c'}, null]

2. 函数泛型

函数泛型允许我们编写可以适用多种类型的函数, 提高代码的灵活性和可复用性。

简单的例子 我们可以实现一个函数createArray, 它可以创建一个指定长度的数组, 并且将每一项填充一个默认值:

typescript
const createArray = (length: number, value: any): any[] => {
  let results: any[] = []
  for (let key = 0, key <= length; key++) {
    results[key] = value
  }
  return results
}

上述示例中, 由于不确定填充值value的类型, 使用了any类型来指定类型, 并且返回值也是any类型的数组, 虽然编译不会报错, 但我们无法得到确定的返回值的类型; 这个时候,泛型就派上用场了:

typescript
  function createArray<T>(length:number, value: T): Array<T> {
    let results: Array<T> = [];
    for (let key = 0, key < length; key++) {
      results[key] = value
    }
    return results
  }
  // 箭头函数
  const createArray = <T>(length: number, value: T): Array<T> => {
    let results: Array<T> = [];
      for (let key = 0, key < length; key++) {
        results[key] = value
      }
    return results
  }
  createArray<string>(3, 'x') // 返回 ['x', 'x', 'x']

我们在函数名后使用了<T>, 其中T表示任意类型, 后面的value参数和函数返回值就可以直接使用Array<T>了。 在调用时,我们传入string类型, 表示指定它的类型为字符串, 当然也可以不指定类型, 由类型推断自动推断出来:

typescript
createArray(3, 'x') // 返回 ['x', 'x', 'x'], 通过‘x’的类型自动·推断出泛型的类型

多个类型参数 定义泛型的时候,可以一次定义多个类型参数:

typescript
  function swap<T, U>(tuple: [T, U]): [U, T] {
    return [tuple[1], tuple[0]];
  }
  swap<number,string>([7, 'seven']); // ['seven', 7]

上述示例中, 我们实现了一个swap函数, 用于交换输入中的元组。

泛型约束 在函数内部使用泛型参数时, 由于不知道参数的具体类型, 所以不能够随意使用它的属性与方法:

typescript
const loggingIdentity: <T>(arg: T) =>T = <T>(arg:T): T => {
  console.log(arg.length);
  return arg
}
loggingIdentity(0)
ts泛型类型不明确

这个时候, 我们可以在函数参数类型上加上一些限制, 只能让它穿入那些包含length属性的变量, 这个限制操作就是泛型约束

typescript
interface Lengthwise {
  length: number;
}

const loggingIdentity: <T extends Lengthwise>(arg: T) =>T = <T extends Lengthwise>(arg:T): T => {
  console.log(arg.length);
  return arg
}

const arr = [1,2,3,4,5]
const num = 5
loggingIdentity(arr)

loggingIdentity(num) // ts编译错误
ts泛型约束限制

多个泛型类型也可以互相约束:

typescript
  const copyFileds = <T extends U, U>(target: T, source: U): T => {
    for (const key in source) {
      target[key] = (<T>source)[key];
    }
    return target;
  }
  const obj1 = { name: '张三' }
  const obj2 = { name: '张三', sex: '' }
  const obj3 = { age: 18 }

  copyFileds(obj2, obj1) //正常编译

  copyFileds(obj3, obj1) //ts编译报错,obj3缺少来自obj2的 name 属性

上述示例中,我们使用TU两个类型字段, 并且指定T继承自U, 这样就保证了T不会出现U不存在的字段

ts泛型以及泛型约束
typescript
const getData = <T extends object, K extends keyof T>(obj: T, key: K):T[K]  => {
  return obj[key]
};
const defaultObj = {name: '张三', age: 18};
getData(defaultObj, 'name')

上述示例中, 我们实现了一个getData的函数, 它的功能是返回目标对象的目标key的值, 关于类型, 我们约定了K的取值范围只能来自终于T的key值, 使用其它的类型会报错

ts泛型以及泛型约束

3. 接口泛型

接口泛型允许我们创建可适用于不同类型的接口定义。例如:

  • 基本用法
typescript
interface IResponse<T, U> {
  str :T,
  num: U
}

const newName: IResponse<string, number> = {str: '11', num: 22}

上述示例中, 我们定义了一个IResponse的接口, 它接受两个类型参数TU, 通过指定类型参数为numberstring,我们创建了一个具体的newName对象, 它的str类型是string,num的类型是number

  • 定义函数的形状

    类型确定情况

    typescript
      interface ISearchFunc {
        (source: string, subString: string): boolean;
      }
      const createArray:ISearchFunc
      createArray = (source: string, subString: string) =>{
        return source.search(subString) !== -1;
      }

    泛型定义函数

    typescript
    interface ICreateArray {
      <T>(length: number, value: T): T[]
    }
    
    const createArray: ICreateArray = <T>(length: number, value: T): Array<T> => {
      const result:T[] = []
      for(let k = 1; k<= length; k++) {
        result[k]  = value
      }
      return result
    }
    
    createArray(3, 'y')

    也可以提前把泛型定义到接口上

    typescript
      interface ICreateArray<T> {
        (length: number, value: T): T[]
      }
    
      const createArray: ICreateArray<unknown> = <T>(length: number, value: T): Array<T> => {
        const result:T[] = []
        for(let k = 1; k<= length; k++) {
          result[k]  = value
        }
        return result
      }
    
    createArray(3, 'y')

    ⚠️注意:此时定义接口类型时候, 需要传入泛型的类型

4. 类泛型

与接口类型相似, 泛型也可以用于类的类型定义中

typescript
  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; };

人生没有捷径,就像到二仙桥必须要走成华大道。