在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定具体类型的一种特性,这样可以极大程度的复用代码

在函数中使用泛型

// 只能创建 数字 类型的数组
function createNumberArray(length: number, value: number): number[] {
  const arr = Array<number>(length).fill(value);
  return arr;
}
// 只能创建 字符串 类型的数组
function createStringArray(length: number, value: string): string[] {
  const arr = Array<string>(length).fill(value);
  return arr;
}

// 使用泛型:可以创建任意类型的数组
function createArray<T>(length: number, value: T): T[] {
  const arr = Array<T>(length).fill(value);
  return arr;
}
const res = createArray<string>(3, "foo");
const res1 = createArray<string | boolean>(3, 'hahaha');
const res2 = createArray<string | boolean>(3, true);

在类中使用泛型

class Log<T> {
  run(value: T) {
    return value;
  }
}

// 指定泛型类是 number,则必须是 number 类型
let log1 = new Log<number>();
log1.run(1);

// 未指定类型,则可以是任意类型
let log2 = new Log();
log2.run("1");

泛型与 new

function factory<T>(type: { new(): T }): T {
  return new type();
}

class Person { }
let p = factory<Person>(Person);
console.log(p); // Person { }

在接口中使用泛型

// 声明泛型
interface Calculate {
  <T>(a: T, b: T): T
}
let sum2: Calculate = function <T>(a: T, b: T): T {
  return a;
};

sum2<number>(1, 2);
// 声明泛型在 接口上(类似柯里化)
interface Calculate2<T> {
  <U>(a: T, b: T): U
}
let sum3: Calculate2<number> = function <U>(a: number, b: number): U {
  return a + b as any;
};

sum3<string>(1, 2);

泛型约束(对泛型的范围进行约束)

// 1.确保 length 属性存在
interface Length {
  length: number;
}

// 类型约束:T 必须有 length 属性
function log<T extends Length>(value: T) {
  return value;
}

log("1");
log([1]);
log(1); // 类型“number”的参数不能赋给类型“Length”的参数
// 2.检查对象上的键是否存在
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" 的参数

// 第二种方式获取对象属性
let varibel = 'name';
let name2 = tsInfo[varibel as keyof typeof tsInfo];

类型别名

type Cart<T> = { list:T[] } | T[];

let c1:Cart<string> = { list: ['1'] };
let c2:Cart<number> = [1, 2, 3]; 

应用场景案例

// 1.防抖
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));
// 2.节流
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. 在类中使用泛型
  3. 3. 泛型与 new
  4. 4. 在接口中使用泛型
  5. 5. 泛型约束(对泛型的范围进行约束)
  6. 6. 类型别名
  7. 7. 应用场景案例