如何在TypeScript中实现类型与请求URL、方法的映射?
解决方案:利用类型标识+运行时映射实现自动请求关联
由于TypeScript类型在编译后会被擦除,无法直接用类型作为Map的键,但可以通过给每个请求参数类型添加唯一的运行时标识字段,结合类型层面的字面量约束,实现类型与请求配置的关联,替代繁琐的instanceof判断。
步骤1:定义基础类型与请求配置
首先定义通用的请求配置结构,以及带标识的请求参数类型模板:
// 通用请求配置类型 type RequestConfig = { url: string; method: 'GET' | 'POST' | 'PUT' | 'DELETE'; }; // 带类型标识的请求参数基类 type TypedRequest<T extends string> = { __type: T; // 唯一的字符串标识,同时作为类型区分符 };
步骤2:定义具体请求参数类型
给每个请求的参数类型添加唯一的__type字面量,确保类型层面唯一:
// 获取用户信息的请求参数 type GetUserRequest = TypedRequest<'getUser'> & { userId: number; }; // 创建用户的请求参数 type CreateUserRequest = TypedRequest<'createUser'> & { username: string; email: string; }; // 更新用户的请求参数 type UpdateUserRequest = TypedRequest<'updateUser'> & { userId: number; username?: string; email?: string; };
步骤3:创建运行时配置映射
用__type的字符串值作为键,映射对应的请求配置:
const requestConfigMap: Record<string, RequestConfig> = { getUser: { url: '/api/users/{userId}', method: 'GET' }, createUser: { url: '/api/users', method: 'POST' }, updateUser: { url: '/api/users/{userId}', method: 'PUT' }, };
步骤4:封装泛型请求函数
利用类型守卫自动推导参数类型,从映射中获取配置,无需手动传入URL和方法:
// 类型守卫:判断参数是否为带__type的请求类型 function isTypedRequest<T extends string>(obj: unknown): obj is TypedRequest<T> { return typeof obj === 'object' && obj !== null && '__type' in obj && typeof (obj as any).__type === 'string'; } // 替换URL中的占位符(如{userId}) function replaceUrlParams(url: string, params: Record<string, any>): string { return url.replace(/\{(\w+)\}/g, (_, key) => String(params[key])); } // 泛型请求函数 async function standardRequest<T extends TypedRequest<string>, R>(params: T): Promise<R> { if (!isTypedRequest(params)) { throw new Error('Invalid request params: missing __type field'); } const config = requestConfigMap[params.__type]; if (!config) { throw new Error(`No config found for request type: ${params.__type}`); } // 处理URL占位符 const url = replaceUrlParams(config.url, params); // 根据method处理参数传递逻辑 const options: RequestInit = { method: config.method }; if (config.method !== 'GET') { options.body = JSON.stringify(params); options.headers = { 'Content-Type': 'application/json' }; } // 发起请求并解析响应 const response = await fetch(url, options); if (!response.ok) { throw new Error(`Request failed: ${response.statusText}`); } return response.json() as Promise<R>; }
步骤5:使用示例
调用时只需传入参数,TypeScript会自动推导类型,函数自动获取对应请求配置:
// 获取用户信息 async function fetchUser(userId: number) { const result = await standardRequest<GetUserRequest, { id: number; username: string; email: string }>({ __type: 'getUser', userId, }); console.log(result); } // 创建用户 async function addUser(username: string, email: string) { const result = await standardRequest<CreateUserRequest, { id: number }>({ __type: 'createUser', username, email, }); console.log(result); }
扩展方式
新增请求时,只需:
- 定义带唯一
__type的请求参数类型 - 在
requestConfigMap中添加对应的配置 - 直接调用
standardRequest即可,无需修改函数逻辑
这种方案既保留了TypeScript的类型校验,又避免了大量if/instanceof判断,扩展性和维护性更强。
内容的提问来源于stack exchange,提问作者Tails128
相关产品推荐
相关产品推荐

