泛型简介

  1. 泛型相当于是一个 类型变量,在定义时,无法预先知道具体的类型,可以用该变量来代替,只有到调用时,才能确定它的类型;

  2. 很多时候 TS 会智能的根据传递的参数,推导出泛型的具体类型;

泛型语法

函数使用泛型

/**
 * 创建一个指定类型的数组
 * @param length 
 * @param value 
 * @returns 
 */
function createArray<T>(length: number, value: T): T[] {
  return Array<T>(length).fill(value);
}

const res = createArray<string>(3, "foo"); // 指定了数组类型是 string
const re2 = createArray(3, "foo"); // 未指定数组类型,会自动识别

类型别名使用泛型

type ObjType<N, G> = { name: N, getAge: () => G };

let obj: ObjType<string, number> = {
  name: '张三',
  getAge() {
    return 20;
  }
}

接口使用泛型

interface Persons<N, G> {
  name: N
  getAge: () => G
}

let user: Persons<string, number> = {
  name: '张三',
  getAge: () => {
    return 20;
  }
}

类使用泛型

// 将泛型放在类上面,里面可以共用同一个泛型
export class ArrayHelper<T> {
  constructor(private arr: T[]) {

  }

  // 发牌
  takeCards(n: number): T[] {
    if (n >= this.arr.length) return this.arr;

    const newArr: T[] = [];
    for (let i = 0; i < n; i++) {
      newArr.push(this.arr[i]);
    }
    return newArr;
  }

  // 洗牌
  shuffle() {
    for (let i = 0; i < this.arr.length; i++) {
      const targetIndex = this.getRandom(0, this.arr.length);
      const temp = this.arr[i];
      this.arr[i] = this.arr[targetIndex];
      this.arr[targetIndex] = temp;
    }
  }

  private getRandom(min: number, max: number) {
    const dec = max - min;
    return Math.floor(Math.random() * dec + max);
  }
}

多泛型

/**
 * 合并两个数组
 * @param arr1 
 * @param arr2 
 * @returns 
 */
function mixinArray<T, K>(arr1: T[], arr2: K[]): (T | K)[] {
  if (arr1.length != arr2.length) throw new Error("两个数组长度不等");

  let result: (T | K)[] = [];
  for (let i = 0; i < arr1.length; i++) {
    result.push(arr1[i]);
    result.push(arr2[i]);
  }

  return result;
}

// 将两个数组进行混合 [1,3,4] + ["a","b","c"] = [1, "a", 3, "b", 4, "c"]
const result = mixinArray([1, 3, 4], ["a", "b", "c"]);
result.forEach(r => {
  console.log(r);
});

泛型约束

TypeScript
TypeScript
// 例子:确保 length 属性存在
interface Length {
  length: number;
}
function log<T extends Length>(param: T) {
  return param.length;
}

log("1");
log([1]);
log(1); // ❌ 类型 “number” 的参数不能赋给类型 “Length” 的参数
// 例子:检查对象上的键是否存在
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

let tsInfo = { name: "zhangsan", age: 20 }
let name = getProperty(tsInfo, 'name');
let habby = getProperty(tsInfo, 'habby'); // ❌ Error:类型 "habby" 的参数不能赋给类型 "name" | "age" 的参数

案例

自定义字典类

TypeScript
TypeScript
// dictionary.ts
export type CallBack<T, U> = (key: T, val: U) => void

export class Dictionary<K, V> {
    private keys: K[] = [];
    private vals: V[] = [];

    get size() {
        return this.keys.length;
    }

    set(key: K, val: V) {
        const i = this.keys.indexOf(key)
        if (i < 0) {
            this.keys.push(key);
            this.vals.push(val);
        }
        else {
            this.vals[i] = val;
        }
    }

    forEach(callback: CallBack<K, V>) {
        this.keys.forEach((k, i) => {
            const v = this.vals[i];
            callback(k, v);
        })
    }

    has(key: K) {
        return this.keys.includes(key);
    }

    delete(key: K) {
        const i = this.keys.indexOf(key);
        if (i === -1) {
            return;
        }
        this.keys.splice(i, 1);
        this.vals.splice(i, 1);
    }
}
// index.ts
import { Dictionary } from "./dictionary";

const dic = new Dictionary<string, number>();

dic.set("a", 1);
dic.set("b", 2);
dic.set("a", 11);
dic.set("c", 33);

dic.forEach((k, v) => {
    console.log(`${k}:${v}`);
})
console.log("当前键值对数量:" + dic.size);
console.log("删除键b")

dic.delete("b");

dic.forEach((k, v) => {
    console.log(`${k}:${v}`);
})
console.log("当前键值对数量:" + dic.size);

防抖

TypeScript
TypeScript
TypeScript
type FN = (...args: any[]) => any;

export function debounce<T extends FN>(fn: T, limit: number = 300): (...arg: Parameters<T>) => ReturnType<T> {
  let timer: any, lastResult: any;

  return (...args: any[]) => {
    clearTimeout(timer);
    timer = setTimeout(() => { lastResult = fn(...args) }, limit);
    return lastResult; // lastResult 一直是 undefined
  }
}

// 测试
const fn = debounce((msg: number) => {
  console.log(msg);
  return msg;
});
new Array(1000).fill(0).forEach(index => fn(index));
/**
 * Parameters 为内置关键字
 *  - 语法:Parameters<typeof 函数名称>
 *  - 获取函数的参数类型,得到的是一个数组
 */
function test(a: string, b: number) {
  return {
    a, b
  }
}

type testtype = Parameters<typeof test>;  // type testtype = [a: string, b: number]
// 获取的是 类型值
type testtype1 = Parameters<typeof test>[1];  // type testtype1 = number
/**
 * ReturnType 为内置关键字
 *  - 语法:ReturnType<typeof 函数>
 *  - 获取函数的返回值类型
 */
interface User {
  name: string,
  age: number,
}

function test(person: User) {
  return person
}

type test = ReturnType<typeof test>;  // User

节流

type FN = (...args: any[]) => any;

export function throttle<T extends FN>(fn: T, limit: number = 300): (...args: Parameters<T>) => ReturnType<T> {
  let lastResult: any;
  let inThtottle: boolean = false;

  return (...args: any[]) => {
    if (!inThtottle) {
      inThtottle = true;
      setTimeout(() => {
        inThtottle = false;
      }, limit);
      lastResult = fn(...args);
    }
    return lastResult; // lastResult 一直是 undefined
  }
}

// 测试
const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
const fn = throttle((msg: string) => console.log('running...', msg));
async function run() {
  for (let index = 0; index < 100; index++) {
    await wait(100);
    fn(index + '');
  }
}
run();
打赏作者
您的打赏是我前进的动力
微信
支付宝
评论

中午好👏🏻,我是 ✍🏻   疯狂 codding 中...

粽子

这有关于前端开发的技术文档和你分享。

相信你可以在这里找到对你有用的知识和教程。

了解更多

目录

  1. 1. 泛型简介
  2. 2. 泛型语法
    1. 2.1. 函数使用泛型
    2. 2.2. 类型别名使用泛型
    3. 2.3. 接口使用泛型
    4. 2.4. 类使用泛型
  3. 3. 多泛型
  4. 4. 泛型约束
  5. 5. 案例
    1. 5.1. 自定义字典类
    2. 5.2. 防抖
    3. 5.3. 节流