前端必备TypeScript—泛型

318 阅读3分钟

泛型

1. 泛型的使用范围

泛型可以用于函数、对象、类...

2. 指定函数参数类型

2.1 单个泛型

根据长度和内容创建一个数组

// 根据参数不同,处理结果不同
// 入参和返回值有映射关系
const getArray = <T>(times: number, val: T): T[] => {
  let result = [];
  for (let i = 0; i < times; i++) {
    result.push(val);
  }
  return result;
};
getArray(3, "a"); // ['abc','abc','abc'] 当我使用的时候可以确定类型

2.2 多个泛型

function swap<T, K>(tuple: [T, K]): [K, T] {
    return [tuple[1], tuple[0]]
}
console.log(swap(['a','b']))

3. 函数标注的方式

元祖交换

3.1 函数类型本身

function swap<T, U>(tuple: [T, U]): [U, T] {
  return [tuple[1], tuple[0]];
}

// 测试
let swapResult = swap(["jw", true]);
let r1 = swapResult[0];
let r2 = swapResult[1];

3.2 类型别名

type ISwap = <T, U>(tuple: [T, U]) => [U, T];

const swap: ISwap = (tuple) => {
  return [tuple[1], tuple[0]];
};

let swapResult = swap(["jw", true]);
let r1 = swapResult[0];
let r2 = swapResult[1];

可以使用类型别名,但是类型别名不能被继承和实现。一般联合类型可以使用类型别名来声明

3.3 使用接口定义函数类型

interface ISwap {
  <T, U>(tuple: [T, U]): [U, T];
}
const swap: ISwap = (tuple) => {
  return [tuple[1], tuple[0]];
};

let swapResult = swap(["jw", true]);
let r1 = swapResult[0];

能使用interface尽量使用interface

4. 泛型接口使用

interface ISum<T> { // 这里的T是使用接口的时候传入
    <U>(a: T, b: T): U // 这里的U是调用函数的时候传入
}
let sum: ISum<number> = (a:number, b:number) => {
    return 3 as any
}

4.1 给函数参数定义类型

type ICallback = <T>(arg: T) => void

let forEach = <T>(arr: T[], callback: ICallback) => {
    for (let i = 0; i< arr.length; i++) {
        callback(arr[i])
    }
}

forEach(['a', 'b', 'c', 1], (item) => {
    console.log(item)
})

4.2 给函数定义类型-使用接口的时候确定的类型


// T泛型放在前面,表示接口使用的时候需要传递类型过来
type ICallback<T> = (arg: T) => void

// 使用类型ICallback时就需要把泛型传递下去ICallback<T>
type IForEach = <T>(arr: T[], callback: ICallback<T>) => void

// 后面的arr,callback会在函数调用时进行类型推导
let forEach: IForEach = (arr, callback) => {
    for (let i = 0; i< arr.length; i++) {
        callback(arr[i]) //  callback 没有执行, 所以无法推导arr[i] = T
    }
}

// 函数调用的时候进行类型推导
forEach(['a', 'b', 'c', 1], (item) => {
    console.log(item)
})

4.3 给函数定义类型-在调用函数的时候确定了类型

type ICallback = <T>(arg: T) => void

type IForEach = <T>(arr: T[], callback: ICallback) => void

// 后面的arr,callback会在函数调用时进行类型推导
let forEach: IForEach = (arr, callback) => {
    for (let i = 0; i< arr.length; i++) {
        callback(arr[i]) //  callback 没有执行, 所以无法推导arr[i] = T
    }
}

// 函数调用的时候进行类型推导
forEach(['a', 'b', 'c', 1], (item) => {
    console.log(item)
})

5. 默认泛型

// 泛型可以指定默认值  默认泛型 给泛型增加了默认值

type Union<T = string> = T | number; // 场景就是如果用户不传入类型 我也希望有默认值

type t1 = Union;
type t2 = Union<boolean>;

6. 泛型约束

6.1 约束传入的泛型类型

A extends B AB的子类型

// 此方法 传入number -》 number
// 传入 string -》 string
// boolean 这是错误的

function handle<T extends number | string>(val: T): T {
  return val;
}
let r1 = handle(123);
let r2 = handle("abc");
// let r3 = handle(true);
interface IWithLength {
  length: number;
}
// abc extends IWithLength 把字符串幻想成一个基于对象扩展的类型
function getLen<T extends IWithLength>(val: T) {
  return val.length;
}
getLen((a: number, b: string) => {});

可以通过下面的方法验证字符串字面量的类型是IWithLength的子类

interface IWithLength {
    length: number;
}

type test = '11' extends IWithLength ? '22': never

6.2 约束索引的签名

// 第二个参数只能是第一个参数中的key
function getVal<T extends object, U extends keyof T>(obj: T, key: U) {
  return obj[key];
}
getVal({ a: 1, b: 2, c: 3 }, "c");

6.3 返回泛型中指定属性

const getVal = <T,K extends keyof T>(obj:T,key:K) : T[K]=>{
    return obj[key];
}
const sum = <T extends number>(a: T, b: T): T => {
    // 强制类型转换
    return (a + b) as T
}
let r = sum<number>(1, 2); 

7. 对象中可以使用泛型

常见的就是描述接口的返回值

// code:200, data:?, message

interface ApiResponse<T = any> {
  code: number;
  data: T; // 坑位
  message?: string;
}
interface LoginRes {
  token: string;
}
function toLogin(): ApiResponse<LoginRes> {
  return {
    code: 200,
    data: {
      token: "Bearer token",
    },
  };
}
let r = toLogin();
r.data.token;

8. 类中可以使用泛型

// 求列表中的最大值 调用的时候可以限制传入的类型
class MyList<T extends number | string> {
  private arr: T[] = [];
  add(val: T) {
    this.arr.push(val);
  }
  getMax(): T {
    let arr = this.arr;
    let max = arr[0];
    for (let i = 0; i < arr.length; i++) {
      let cur = arr[i];
      cur > max ? (max = cur) : void null;
    }
    return max;
  }
}
let list = new MyList();
list.add(1);
list.add(100);