chengaofeng
发布于 2024-07-18 / 17 阅读
0
0

Typescript泛型

为什么要使用泛型?泛型定义

泛型的特点

  1. 泛型的宽泛:定义时不明确使用时必须明确成某种具体数据类型的数据类型

  2. 泛型的严谨:编译期间进行数据类型检查的数据类型

  3. 泛型形参类型一般有两种表示:1. A-Z任何一个字母 2. 语义化的单词来表示

class ArrayList<T> {
  array: Array<T>
}

// 泛型接口
interface Ref<T> {
  value: T
}

const ref:Ref<number> = {
  value: 5
}

泛型的应用ArrayList

class ArrayList<T> {
  private arr: Array<T>;

  constructor(arr: Array<T> = []) {
    this.arr = arr;
  }

  add(item: T) {
    this.arr.push(item);
  }

  get(index: number): T {
    return this.arr[index];
  }

  size(): number {
    return this.arr.length;
  }

  remove(index: number): T {
    return this.arr.splice(index, 1)[0];
  }

  isEmpty(): boolean {
    return this.arr.length === 0;
  }

  toString(): string {
    return this.arr.toString();
  }

  toArray(): Array<T> {
    return this.arr;
  }

  clear() {
    this.arr = [];
  }

  contains(item: T): boolean {
    return this.arr.includes(item);
  }

  indexOf(item: T): number {
    return this.arr.indexOf(item);
  }

  lastIndexOf(item: T): number {
    return this.arr.lastIndexOf(item);
  }

  removeItem(item: T): boolean {
    const index = this.indexOf(item);
    if (index === -1) return false;
    this.remove(index);
    return true;
  }
}

let arrList = new ArrayList<number>();
arrList.add(1);
arrList.add(2);
arrList.add(3);
arrList.add(4);
console.log(arrList.toString()); // 1,2,3,4

export default ArrayList;

泛型默认值

// 使用时不给类型的话,就会使用默认值any
class ArrayList<T = any> {
  array: Array<T>
}

泛型约束

  1. keyof 表示获取一个类或者一个对象类型或者一个接口类型的所有属性名key组成的联合类型

  2. 索引访问类型 T[k]

class ArrayList<T> {
  private arr: Array<T>;

  constructor(arr: Array<T> = []) {
    this.arr = arr;
  }

  add(item: T) {
    this.arr.push(item);
  }
}
// 类的索引访问类型获取
type Arr = ArrayList<string>["arr"];
class ArrayList<T> {
  public arr: Array<T>;

  constructor(arr: Array<T> = []) {
    this.arr = arr;
  }

  add(item: T) {
    this.arr.push(item);
  }

  get(index: number): T {
    return this.arr[index];
  }

  size(): number {
    return this.arr.length;
  }

  remove(index: number): T {
    return this.arr.splice(index, 1)[0];
  }

  isEmpty(): boolean {
    return this.arr.length === 0;
  }

  toString(): string {
    return this.arr.toString();
  }

  toArray(): Array<T> {
    return this.arr;
  }

  clear() {
    this.arr = [];
  }

  contains(item: T): boolean {
    return this.arr.includes(item);
  }

  indexOf(item: T): number {
    return this.arr.indexOf(item);
  }

  lastIndexOf(item: T): number {
    return this.arr.lastIndexOf(item);
  }

  removeItem(item: T): boolean {
    const index = this.indexOf(item);
    if (index === -1) return false;
    this.remove(index);
    return true;
  }
}

// 获取 ArrayList 的Key类型
type ArrayListKey<T> = keyof ArrayList<T>;
// 公有属性和方法名称可以被keyof取到
const str: ArrayListKey<string> = "removeItem";
// 写一个通用获取类的属性联合类型的工具
type ClassKeys<T> = keyof T;
// 获取ArrayList的所有属性和方法
type ArrayListKeys = ClassKeys<ArrayList<string>>;
// 这样写的话ClassKeys工具存在一个问题,就是泛型T可以是任意类型, 但是我们只想获取对象、类的属性和方法
type stringKeys = ClassKeys<string>;
// 这个时候我们可以使用extends关键字来约束泛型T
type ClassKeys2<T extends object> = keyof T;
type stringKeys2 = ClassKeys2<string>; // 报错 类型“string”不满足约束“object”

泛型在Vue3源码中的应用

// 这里的泛型约束首先保证T是一个对象类型,才可以使用keyof T获取T的key类型,然后再使用extends keyof T约束K是T的key类型
class ObjectRefImpl<T extends object, K extends keyof T> {}
const Obj = { username: "lison", age: 18 };
const objRef = new ObjectRefImpl<typeof Obj, "username">(); // 第二个泛型参数只能是Obj的key类型

extends keyof + keyof的联合应用

interface IArrayList<T extends object, K> {
  test: K extends keyof T ? T[K] : never;
}

type TestType = IArrayList<{ name: string; age: number }, "name">['test']; // string
type TestType2 = IArrayList<{ name: string; age: number }, "age">['test']; // number

泛型反向赋值

typescript可以根据传递的值反向推断泛型的值!

TypeScript 的泛型反向赋值(Generic Contravariance)是指在泛型编程中,泛型类型参数的赋值方向与其声明的继承方向相反的情况。这主要出现在函数参数类型的上下文中,即一个函数参数类型可以接受更一般(更基础)类型的值作为输入。

在 TypeScript 中,类型系统设计为结构性的,这意味着类型的兼容性是通过成员来决定的。在函数参数类型的上下文中,如果类型 A 的每个成员都能在类型 B 中找到对应的兼容成员,那么 B 可以被认为是 A 的子类型(`B` extends A),即 B 可以赋值给 A

对于泛型反向赋值,考虑以下情况:

function processItems<T>(items: T[]): void {

    // 处理项目

}

let numbers: Array<number> = [1, 2, 3];

let objects: Array<{ value: number }> = [{ value: 1 }, { value: 2 }];

processItems(numbers); // 正确:T 被推断为 number

processItems(objects); // 正确:T 被推断为 { value: number }

在这个例子中,`processItems` 函数接受一个类型为 T[] 的数组。当我们传递一个 number[]{ value: number }[] 给这个函数时,`T` 被相应地推断为 number{ value: number }

然而,泛型反向赋值的概念通常与函数参数的类型协变(Covariance)和逆变(Contravariance)相关,特别是在高级类型操作和高阶函数中更为明显。在 TypeScript 中,函数参数是逆变的,这意味着如果你有一个类型为 (base: Base) => void 的函数,你可以将它赋值给一个类型为 (derived: Derived) => void 的变量,前提是 Derived 继承自 Base。这是因为从类型安全的角度来看,函数能够接受更具体的类型作为参数是安全的。

TypeScript 的类型系统非常灵活,支持协变、逆变以及双向协变(Bivariance),这些特性使得 TypeScript 在处理复杂类型关系时非常强大。


评论