为什么要使用泛型?泛型定义
泛型的特点
泛型的宽泛:定义时不明确使用时必须明确成某种具体数据类型的数据类型
泛型的严谨:编译期间进行数据类型检查的数据类型
泛型形参类型一般有两种表示: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>
}
泛型约束
keyof 表示获取一个类或者一个对象类型或者一个接口类型的所有属性名key组成的联合类型
索引访问类型 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 在处理复杂类型关系时非常强大。