泛型简介
泛型相当于是一个 类型变量,在定义时,无法预先知道具体的类型,可以用该变量来代替,只有到调用时,才能确定它的类型;
很多时候 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);
});
泛型约束
// 例子:确保 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" 的参数
案例
自定义字典类
// 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);
防抖
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();
📱 常见问题
上一篇