当前位置: 首页 > news >正文

Typescript 泛型

Typescript 泛型

一、讲一讲什么是泛型

TypeScript 中的泛型是一种强大的工具,它允许你在定义函数、类或接口时使用类型变量,这些类型变量可以在使用时被具体的类型所替代。泛型的主要作用是创建可重用的组件,同时保持类型安全。

1.1 为什么需要泛型?

假设你需要创建一个函数,它返回传入的任何值。在不使用泛型的情况下,你可能会这样写:

function identity(arg: any): any {return arg;
}

虽然这个函数可以工作,但它失去了类型信息。如果传入一个 number,返回值的类型是 any,而不是 number。泛型可以解决这个问题。

1.2 泛型函数

泛型函数使用类型变量(通常用 T 表示)来捕获调用时传入的类型:

function identity<T>(arg: T): T {return arg;
}// 使用方式
const num = identity<number>(123); // 指定 T 为 number
const str = identity("hello");     // 类型推断,T 为 string
  • 类型变量 T:代表任意类型,在调用时确定具体类型。
  • 类型推断:可以省略 <number>TypeScript 会自动推断类型。

1.3 泛型接口

泛型可以用于接口定义:

interface GenericIdentityFn<T> {(arg: T): T;
}const myIdentity: GenericIdentityFn<number> = identity;

这里 GenericIdentityFn<T> 是一个泛型接口,它接受一个类型参数 T

1.4 泛型类

泛型类的语法类似泛型接口:

class GenericNumber<T> {zeroValue: T;add: (x: T, y: T) => T;constructor(zeroValue: T, add: (x: T, y: T) => T) {this.zeroValue = zeroValue;this.add = add;}
}// 使用示例
const numGenerator = new GenericNumber<number>(0, (x, y) => x + y);
const strGenerator = new GenericNumber<string>("", (x, y) => x + y);

1.5 泛型约束

有时你需要限制泛型的类型范围,例如要求类型必须具有某个属性:

interface Lengthwise {length: number;
}function loggingIdentity<T extends Lengthwise>(arg: T): T {console.log(arg.length); // 现在可以访问 length 属性return arg;
}loggingIdentity("hello"); // 合法,string 有 length 属性
loggingIdentity([1, 2, 3]); // 合法,数组有 length 属性
// loggingIdentity(123); // 错误,number 没有 length 属性

1.6 泛型参数的默认类型

可以为泛型参数指定默认类型:

function createArray<T = number>(length: number, value: T): T[] {return Array(length).fill(value);
}const numbers = createArray(3, 0); // T 默认为 number
const strings = createArray<string>(3, "x"); // 指定 T 为 string

1.7 泛型工具类型

TypeScript 内置了一些常用的泛型工具类型:

  • Partial<T>:将类型 T 的所有属性变为可选。
  • Readonly<T>:将类型 T 的所有属性变为只读。
  • Pick<T, K>:从类型 T 中选取部分属性 K
  • Exclude<T, U>:从 T 中排除可以赋值给 U 的类型。
type User = { name: string; age: number; email: string };type PartialUser = Partial<User>; // { name?: string; age?: number; email?: string }
type ReadonlyUser = Readonly<User>; // { readonly name: string; ... }
type NameAndAge = Pick<User, "name" | "age">; // { name: string; age: number }

二、泛型在实际开发中有哪些常见的应用场景

2.1 数据结构与容器

泛型常用于实现通用的数据结构,如数组、链表、栈、队列等,使其可以存储任意类型的数据,同时保持类型安全。

示例:泛型数组

// 内置的 Array<T> 是泛型
const numbers: Array<number> = [1, 2, 3];
const strings: Array<string> = ["a", "b", "c"];// 自定义泛型容器
class Stack<T> {private items: T[] = [];push(item: T) {this.items.push(item);}pop(): T | undefined {return this.items.pop();}
}const numberStack = new Stack<number>();
numberStack.push(1);
const num = numberStack.pop(); // 类型为 number

示例:泛型链表

class LinkedListNode<T> {value: T;next: LinkedListNode<T> | null;constructor(value: T, next: LinkedListNode<T> | null = null) {this.value = value;this.next = next;}
}const numberNode = new LinkedListNode<number>(1); // 类型为 number

示例:泛型队列

class Queue<T> {private items: T[] = [];enqueue(item: T) {this.items.push(item);}dequeue(): T | undefined {return this.items.shift();}
}const numberQueue = new Queue<number>();
numberQueue.enqueue(1);
const num = numberQueue.dequeue(); // 类型为 number

2.2 API 请求与响应处理

在前后端交互中,API 返回的数据结构通常是通用的,但具体类型不同。泛型可以确保响应数据的类型安全。

示例:通用 API 响应类型

interface ApiResponse<T> {code: number;message: string;data: T;
}// 获取用户列表
function fetchUsers(): Promise<ApiResponse<User[]>> {return fetch("/api/users").then(res => res.json());
}// 获取单个用户
function fetchUser(id: string): Promise<ApiResponse<User>> {return fetch(`/api/users/${id}`).then(res => res.json());
}

2.3 高阶组件与函数

泛型可用于创建复用性高的高阶组件或工具函数,这些组件 / 函数可以处理多种类型的数据。

示例:通用状态管理钩子

import { useState } from "react";function useLocalStorage<T>(key: string, initialValue: T) {const [value, setValue] = useState<T>(() => {const stored = localStorage.getItem(key);return stored ? JSON.parse(stored) : initialValue;});const setStoredValue = (newValue: T | ((prev: T) => T)) => {const finalValue = typeof newValue === "function" ? (newValue as (prev: T) => T)(value) : newValue;localStorage.setItem(key, JSON.stringify(finalValue));setValue(finalValue);};return [value, setStoredValue] as const;
}// 使用方式
const [user, setUser] = useLocalStorage<User>("currentUser", { name: "", age: 0 });

2.4 类型安全的工具函数

泛型可以让工具函数适用于多种类型,同时保持类型检查。

示例:类型安全的深拷贝函数

function deepClone<T>(obj: T): T {return JSON.parse(JSON.stringify(obj));
}const original = { name: "Alice", age: 30 };
const clone = deepClone(original); // 类型为 { name: string; age: number }

2.5 接口与抽象类的多态实现

泛型可以让接口或抽象类在实现时支持不同的具体类型。

示例:泛型仓储接口

interface Repository<T> {findById(id: string): Promise<T | null>;save(entity: T): Promise<T>;delete(id: string): Promise<void>;
}// 用户仓储实现
class UserRepository implements Repository<User> {async findById(id: string) {// ...}// ...
}

2.6 事件系统与发布 - 订阅模式

泛型可以确保事件数据的类型一致性。

示例:泛型事件总线

type EventMap = Record<string, any>;class EventEmitter<T extends EventMap> {private events: Partial<{ [K in keyof T]: Array<(data: T[K]) => void> }> = {};on<K extends keyof T>(event: K, listener: (data: T[K]) => void) {if (!this.events[event]) this.events[event] = [];this.events[event]?.push(listener);}emit<K extends keyof T>(event: K, data: T[K]) {this.events[event]?.forEach(listener => listener(data));}
}// 使用方式
type AppEvents = {"user:created": User;"order:placed": Order;
};const emitter = new EventEmitter<AppEvents>();
emitter.on("user:created", (user) => {// user 类型为 User
});
emitter.emit("order:placed", { id: "123", amount: 99.99 }); // 类型检查

2.7 泛型工具类型的应用

TypeScript 内置的泛型工具类型(如 Partial<T>Pick<T, K>)在实际开发中经常用于类型转换。

示例:表单数据处理

type User = {id: string;name: string;age: number;email: string;
};// 表单数据通常是可选的
type UserFormData = Partial<User>;// 从 User 中选取部分字段
type UserPublicInfo = Pick<User, "name" | "email">;

三、 泛型的类型变量的作用

在 TypeScript 中,泛型的类型变量(如 T、U、K 等)是泛型系统的核心机制,它们允许你创建可复用的组件,同时保持类型安全。以下是类型变量的详细作用和应用场景:

3.1 捕获调用时的类型

类型变量的主要作用是捕获用户使用泛型时传入的具体类型,并在整个泛型定义中使用该类型。

示例:

function identity<T>(arg: T): T {return arg;
}const num = identity<number>(123); // T 被捕获为 number
const str = identity("hello");     // 类型推断:T 为 string
  • T 捕获了传入参数的类型,并将其作为返回值的类型。

3.2 实现类型参数化

类型变量使函数、类或接口能够接受任意类型的参数,从而实现代码复用。

示例:泛型数组处理函数

function firstElement<T>(arr: T[]): T | undefined {return arr[0];
}const nums = firstElement([1, 2, 3]); // T 为 number,返回值类型为 number | undefined
const strs = firstElement(["a", "b"]); // T 为 string,返回值类型为 string | undefined
  • 函数可以处理任意类型的数组,而不需要为每种类型单独实现。

3.3 类型变量的约束与关联

类型变量可以相互约束,确保类型之间的一致性。

示例:泛型键值对

function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {return obj[key];
}const user = { name: "Alice", age: 30 };
const name = getProperty(user, "name"); // T 为 User,K 为 "name",返回值类型为 string
// const invalid = getProperty(user, "email"); // 错误:"email" 不是 User 的键
  • K extends keyof T 约束 K 必须是 T 的键之一。

3.4 泛型类与接口的类型变量

类型变量可以用于类或接口的定义,使其能够处理不同类型的数据。

示例:泛型字典

interface Dictionary<T> {[key: string]: T;
}const numbers: Dictionary<number> = { a: 1, b: 2 };
const strings: Dictionary<string> = { hello: "world" };
  • Dictionary<T> 接口可以存储任意类型的值,但所有值的类型必须一致。

3.5 类型变量的默认值

可以为类型变量指定默认类型,使泛型在使用时更加灵活。

示例:带默认类型的泛型

function createArray<T = number>(length: number, value: T): T[] {return Array(length).fill(value);
}const numbers = createArray(3, 0); // T 默认为 number
const strings = createArray<string>(3, "x"); // 指定 T 为 string

3.6 类型变量的作用域

示例:

function identity<T>(arg: T): T {// T 仅在函数内部有效return arg;
}// T 在此处不存在

3.7 多个类型变量的协作

泛型可以使用多个类型变量,它们之间可以相互关联或约束。

示例:泛型映射函数

function map<T, U>(arr: T[], fn: (item: T) => U): U[] {return arr.map(fn);
}const numbers = [1, 2, 3];
const strings = map(numbers, (n) => n.toString()); // T 为 number,U 为 string
  • T 表示输入数组的元素类型,U 表示映射后的元素类型。

3.6 类型变量在工具类型中的应用

TypeScript 内置的工具类型(如 Partial<T>Exclude<T, U>)大量使用泛型类型变量。

示例:

type Partial<T> = {[P in keyof T]?: T[P];
};type User = { name: string; age: number };
type PartialUser = Partial<User>; // { name?: string; age?: number }
  • T 是传入的原始类型,P 是遍历 T 的键。

四、泛型在Vue中的应用

Vue 3(结合 TypeScript)中,泛型的应用场景非常广泛,它可以显著提升类型安全性和代码复用性。

4.1 组件 propsemits 的类型定义

泛型可用于精确指定 propsemits 的类型,避免手动重复定义类型。

4.1.1 泛型 props 定义

使用 DefineComponentdefineComponent 时,可以通过泛型参数指定 props 类型:

import { defineComponent } from 'vue';interface Props {message: string;count: number;
}const MyComponent = defineComponent<Props>({props: {message: { type: String, required: true },count: { type: Number, default: 0 },},setup(props) {// props.message 类型为 string// props.count 类型为 number},
});
4.1.2 泛型 emits 定义

使用 defineEmits 时,可以通过泛型参数指定事件类型:

const MyComponent = defineComponent({emits: ['change', 'submit'] as const,setup(props, { emit }) {// 使用泛型约束 emit 参数类型const handleChange = (value: string) => {emit('change', value); // 类型检查:value 必须为 string};},
});

4.2 refreactivecomputed 的类型推导

泛型可用于明确指定响应式数据的类型,特别是在初始值为 null 或需要更精确类型时。

4.2.1 ref 的泛型用法
import { ref, Ref } from 'vue';// 明确指定 ref 类型
const count = ref<number | null>(null); // count.value 类型为 number | null
count.value = 123; // 类型检查通过// 复杂类型
interface User {name: string;age: number;
}// 明确指定 ref 类型为 User
const user = ref<User>({ name: 'Alice', age: 30 }); // user.value 类型为 User
4.2.2 reactive 的泛型用法

reactive 会自动推导对象的类型,并返回一个响应式版本:

import { reactive } from 'vue';// 自动推导类型
const state = reactive({name: 'Alice',age: 30,address: {city: 'Beijing',street: '123 Main St'}
});// 类型推导结果:
// state: {
//   name: string;
//   age: number;
//   address: {
//     city: string;
//     street: string;
//   };
// }
interface User {name: string;age: number;hobbies?: string[];
}const user = reactive<User>({name: 'Bob',age: 25,hobbies: ['reading', 'swimming']
});// 类型检查:
user.name = 123; // 错误:不能将 number 赋值给 string
user.hobbies?.push('running'); // 正确
4.2.3 computed 的泛型用法
import { computed, ComputedRef } from 'vue';const count = ref(0);
const double: ComputedRef<number> = computed(() => count.value * 2);

4.3 组合式函数(Composables)的泛型设计

泛型使组合式函数可以处理多种类型的数据,增强复用性。

4.3.1 通用状态管理
import { ref, Ref } from 'vue';
// 通用状态管理
function useLocalStorage<T>(key: string, initialValue: T) {// 从本地存储获取初始值const storedValue = localStorage.getItem(key);// 类型断言const initialValueWithType: T = storedValue ? JSON.parse(storedValue) : initialValue;// 类型断言const value = ref<T>(initialValueWithType) as Ref<T>;const setValue = (newValue: T) => {value.value = newValue; // 类型检查:newValue 必须为 TlocalStorage.setItem(key, JSON.stringify(newValue)); // 类型检查:newValue 必须为 T};return { value, setValue };
}// 使用方式
const { value: user } = useLocalStorage<User>('user', { name: '', age: 0 });// user.value 类型为 User
4.3.2 通用 API 请求
import { ref, Ref } from 'vue';
import axios from 'axios';interface ApiResponse<T> {data: T;message: string;
}interface User {name: string;age: number;
}
// 通用 API 请求
function useApi<T>(url: string) {const data = ref<T | null>(null); // data.value 类型为 T | nullconst loading = ref(true); // loading.value 类型为 booleanconst error = ref<string | null>(null); // error.value 类型为 string | null// 类型断言const dataWithType = data as Ref<T>;// 类型断言const loadingWithType = loading as Ref<boolean>;// 类型断言const errorWithType = error as Ref<string | null>;// 类型断言const fetchData = async () => { try {const response = await axios.get<ApiResponse<T>>(url); // 类型检查:response.data 必须为 ApiResponse<T>dataWithType.value = response.data.data; // 类型检查:response.data.data 必须为 T  } catch (err) {errorWithType.value = err.message; // 类型检查:err.message 必须为 string} finally {loadingWithType.value = false; // 类型检查:loadingWithType.value 必须为 boolean}};return { data, loading, error, fetchData };
}// 使用方式
const { data, loading, error, fetchData } = useApi<User[]>('/api/users');// data.value 类型为 User[] | null

4.4 自定义指令的泛型

泛型可用于约束自定义指令绑定值的类型。

import { Directive } from 'vue';interface ScrollOptions {threshold: number; // 类型检查:threshold 必须为 numbercallback: () => void; // 类型检查:callback 必须为函数}
// 自定义指令的泛型
const vScroll: Directive<HTMLElement, ScrollOptions> = { mounted(el, binding) {// binding.value 类型为 ScrollOptionsconst options = binding.value; // ...},
};

4.5 Vue Router 的类型安全

Vue Router 4 结合 TypeScript 使用泛型增强路由参数和查询的类型安全。

4.5.1 路由参数类型
import { createRouter, createWebHistory } from 'vue-router'; 
import { useRoute } from 'vue-router';
// 路由参数类型
interface RouteParams {id: string;
}
// 路由配置类型
const router = createRouter({history: createWebHistory(), // 路由历史模式// 路由配置routes: [{path: '/user/:id', // 路由路径name: 'User', // 路由名称component: UserComponent, // 路由组件},],
});// 在组件中使用
const route = useRoute<RouteParams>(); // 类型检查:route.params 必须为 RouteParams
const userId = route.params.id; // 类型为 string
4.5.2 导航守卫的泛型
// 路由查询类型
interface RouteQuery {name: string;
}// 路由组件类型
interface RouteComponent {name: string;
}// 路由配置类型
interface RouteConfig {path: string;name: string;component: RouteComponent;
}// 导航守卫的泛型
router.beforeEach((to, from) => {// to.params 和 from.params 的类型会根据路由定义自动推导// 类型检查:to.params 必须为 RouteParams// 类型检查:from.params 必须为 RouteParams// 类型检查:to.query 必须为 RouteQuery// 类型检查:from.query 必须为 RouteQuery// 类型检查:to.component 必须为 RouteComponent// 类型检查:from.component 必须为 RouteComponent// 类型检查:to 必须为 RouteConfig// 类型检查:from 必须为 RouteConfigreturn true;
});

4.6 Vuex/Pinia 状态管理

泛型可用于增强状态管理库的类型安全。

4.6.1 Pinia Store
import { defineStore } from 'pinia';
// `Pinia Store` 类型
interface State {items: string[]; // 类型检查:items 必须为 string[]loading: boolean; // 类型检查:loading 必须为 boolean
}// `Pinia Store` 实例
const useMyStore = defineStore<string, State>('myStore', {// `Pinia Store` 状态state: () => ({ items: [], // 类型检查:items 必须为 string[]loading: false, // 类型检查:loading 必须为 boolean}),// `Pinia Store` 方法actions: {// `Pinia Store` 方法类型addItem(item: string) {this.items.push(item); // 类型检查:item 必须为 string},},
});

4.7 插件和高阶组件的泛型设计

泛型可用于创建适用于多种组件类型的插件或高阶组件。

4.7.1 高阶组件(HOC
import { Component, defineComponent } from 'vue';
import { ref } from 'vue';// 高阶组件(`HOC`)类型
function withLoading<T extends Component>(Component: T) {// 高阶组件(`HOC`)类型return defineComponent({components: { Component }, // 类型检查:Component 必须为 T// 高阶组件(`HOC`)属性props: {loading: {type: Boolean, // 类型检查:loading 必须为 booleandefault: false, // 类型检查:loading 默认值必须为 boolean},},setup(props) {const loading = ref(props.loading); // 类型检查:props.loading 必须为 boolean // ...return { loading };},template: `<div><Component v-if="!loading" v-bind="$$attrs" /><div v-else>Loading...</div></div>`,});
}

文章转载自:
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://www.dtcms.com/a/281593.html

相关文章:

  • 智慧跳绳全方案:三模无线+姿态感知,低功耗高精度芯片选型指南OM6626/SI24R2E
  • 【flex布局】
  • Python实现按数字命名合并文本文件
  • 推客小程序开发全景指南:从商业模式到用户体验的完整方法论
  • 前端开发数据缓存方案详解
  • Spring Boot全局异常处理:打造坚如磐石的应用防线
  • C++ - 仿 RabbitMQ 实现消息队列--muduo快速上手
  • 【每日刷题】螺旋矩阵
  • 【Python】定时器快速实现
  • 并发编程-volatile
  • Python学习之路(十二)-开发和优化处理大数据量接口
  • git基础命令
  • Redis学习系列之——Redis Stack 拓展功能
  • 为什么市场上电池供电的LoRa DTU比较少?
  • redisson tryLock
  • React源码5 三大核心模块之一:render,renderRoot
  • MMYSQL刷题
  • 北京-4年功能测试2年空窗-报培训班学测开-第五十一天
  • Typecho插件开发:优化文章摘要处理短代码问题
  • 【跟我学YOLO】(2)YOLO12 环境配置与基本应用
  • PID(进程标识符,Process Identifier)是什么?
  • Markdown编辑器--editor.md的用法
  • GTSuite许可管理
  • 学习日志10 python
  • 【鲲苍提效】全面洞察用户体验,助力打造高性能前端应用
  • JAVA青企码协会模式系统源码支持微信公众号+微信小程序+H5+APP
  • vlan作业
  • CommunityToolkit.Mvvm IOC 示例
  • 【Java】JUC并发(线程的方法、多线程的同步并发)
  • 定时器更新中断与串口中断