Skip to content

泛型

泛型(generic)是 TypeScript 中一个非常重要的特性, 它允许我们编写可以处理多种类型的通用代码, 同时还能保持类型的安全性。

什么是泛型:可以传递类型的参数

泛型就是可以携带类型的变量

ts
泛型参数 规范:要求都是 大写字母:KTVPS、R为泛型
shell
1. 泛型接口
2. 泛型类
3. 泛型约束

泛型背景

下面创建一个函数,

实现功能: 根据指定的数量 count 和数据 value , 创建一个包含 countvalue 的数组

不用泛型的话,这个函数可能是下面这样:

ts
function createArray (count:number,value:any):any[]{
  const arr:any[] = []
  for (let index = 0; index < count; index++) {
    arr.push(value)
  }
  return arr
}

const arr01 = createArray(3,'hello')
const arr02 = createArray(3,100)

console.log(arr01[0].split('')) //运行不报错,但编码时没有提示
console.log(arr02[0].toFixed(3)) //运行不报错,但编码时没有提示
console.log(arr02[0].split('')) //运行报错,但编码时没有提示错误

通过泛型函数可以实现在编码时提示

ts
function createArray <P>(count:number,value:P):P[]{
  const arr:P[]= []
  for (let index = 0; index < count; index++) {
    arr.push(value)
  }
  return arr
}

const arr03 = createArray<string>(3,'hello')
const arr04 = createArray<number>(3,100)
console.log(arr03[0].split('')) 
console.log(arr04[0].toFixed(1)) 
console.log(arr04[0].split('')) //error 类型“number”上不存在属性“split”

泛型参数

可以传递类型的参数,称之泛型参数

tsx
# 泛型参数,要求都是 大写字母:KTVPSR

function createArr<K>(num: number, item: K): K[] {
    let arr: K[] = [];
    for (let i = 0; i < num; i++) {
        arr[i] = item;
    }
    return arr;
}

console.log(createArr<string>(3, 'a')); // ['a','a','a']
console.log(createArr<number>(2, 99)); // [99,99]

多个泛型参数的函数

一个函数可以定义多个泛型参数

ts
function createArray <T,P> (a: T, b: P): [T, P] {
  return [a, b]
}

const result = createArray<string, number>('abc', 123)
console.log(result[0].length)
console.log(result[1].toFixed())

泛型接口

什么时候使用:如果一个对象的类型是实时变化的,就需要泛型作为参数来定义这个对象的类型。

在定义接口时, 如果接口中对象类型不确定,就需要给接口中的属性或方法定义为泛型类型;在使用接口时, 再指定具体的泛型类型(传参,这个参数是一个接口类型)。

ts
// 用泛型来限定接口类型(接口传一个泛型来限定接口的类型)
interface IResponse<T> {
    code: number;
    msg: string;
    data: T
}

interface IBook {
    id: string;
    name: string;
    author: string;
}

interface ITodo {
    id: string;
    title: string;
    isDone: boolean;
}

let data1: IResponse<IBook> = {
    code: 200,
    msg: '成功',
    data: {
        id: 'adfd123',
        name: '葵花宝典',
        author: '东方不败'
    }
}

let data2: IResponse<ITodo> = {
    code: 200,
    msg: '获取数据成功',
    data: {
        id: '123',
        title: '吃饭',
        isDone: true
    }
}

泛型类

在定义类时, 为类中的属性或方法定义为泛型类型 ;在创建类的实例时, 再指定特定的泛型类型(传参,参数是一个接口)

ts
// 泛型类:用泛型限定类
class Container<T>{
    //用于储存数据
    store: T[];
    constructor(store: T[]) {
        this.store = store;
    }
}

let books = new Container<IBook>([
    { id: 'ab123', name: '西游记', author: '吴承恩' },
    { id: 'sx23', name: '三国演义', author: '罗贯中' }
])

let todos = new Container<ITodo>([
    { id: 'sfre1', title: 'chifan', isDone: true }
])

泛型约束

如果我们直接对一个泛型类型参数取 length 属性, 会报错, 因为这个泛型根本就不知道它有这个属性

ts
// 没有泛型约束
function fn <T>(x: T): void {
  // console.log(x.length)  // error 需要保证x有length属性,如何保证x有length属性
}

我们可以使用泛型约束来实现

ts
// 如何保证x有length属性,=> 让x的类型为泛型T的类型,指定泛型T必须拥有length属性。

// 定义一个接口,这个接口有length属性
interface Lengthwise {
    length: number;
}
/**
 *  前提条件:泛型是一种特殊类型的接口
 */

// 让泛型T继承这个接口,那么泛型T就有了length属性,
// 通过(x: T)这样就限定x必须要有length属性

function fn2<T extends Lengthwise>(x: T): void {
    console.log(x.length)
}

fn2('abc')
// fn2(123);
fn2([1,2,3])