如何编写带类型的函数统一管理API路由,消除冗余重复代码?
实现思路与最终代码
核心优化逻辑:
- 直接复用枚举值作为路由模板,无需额外维护映射表,彻底消除重复定义
- 编写通用的占位符替换逻辑,自动匹配路由中的
:xxx格式参数,支持任意数量的参数填充 - 可选增加类型约束,保证传入的参数数量和路由需要的参数数量一致,避免运行时报错
小修复:原代码中判断环境的写法存在问题,
process.env是全局环境变量对象,需要取对应字段比如process.env.NODE_ENV来判断环境
const apiAddress = process.env.NODE_ENV === 'PRODUCTION' ? 'https://some-address.com' : 'http://localhost:5000'; export enum targetsEnum { transcribe = '/services/transcribe', transcribeId = '/services/transcribe/:id', // 可随意新增多参数路由,不需要修改其他逻辑 transcribeDetail = '/services/transcribe/:id/detail/:detailId' } export type ApiTargets = `${targetsEnum}`; // 通用路径参数替换函数 const fillPathParams = (path: string, params: string[] = []): string => { // 匹配所有:开头的占位符 const paramPattern = /:([^/]+)/g; const placeholderCount = (path.match(paramPattern) || []).length; // 运行时参数校验,可根据需要开关 if (params.length !== placeholderCount) { throw new Error(`路由${path}需要${placeholderCount}个参数,实际传入${params.length}个`); } return path.replace(paramPattern, () => params.shift() as string); }; export const getApiAddress = <T extends ApiTargets>(target: T, ...ids: string[]): string => { const filledPath = fillPathParams(target, ids); return `${apiAddress}${filledPath}`; };
使用示例
// 无参数路由调用 getApiAddress(targetsEnum.transcribe) // 输出:http://localhost:5000/services/transcribe // 单参数路由调用 getApiAddress(targetsEnum.transcribeId, '123') // 输出:http://localhost:5000/services/transcribe/123 // 多参数路由调用 getApiAddress(targetsEnum.transcribeDetail, '123', '456') // 输出:http://localhost:5000/services/transcribe/123/detail/456
可选:增强类型校验
如果需要编译阶段就检测参数数量是否匹配,可以新增以下类型逻辑,少传、多传参数都会直接触发TS报错:
// 提取路径中的参数数量类型 type ExtractParamCount<T extends string, Count extends any[] = []> = T extends `${string}:${string}/${infer Rest}` ? ExtractParamCount<Rest, [...Count, 1]> : T extends `${string}:${string}` ? [...Count, 1]['length'] : Count['length']; // 改造getApiAddress的参数类型 export const getApiAddress = <T extends ApiTargets>( target: T, ...ids: ExtractParamCount<T> extends 0 ? [] : string[] & {length: ExtractParamCount<T>} ): string => { const filledPath = fillPathParams(target, ids); return `${apiAddress}${filledPath}`; };
改造后完全符合需求:
- 无重复定义:所有路由仅需要在
targetsEnum中定义一次,修改路由仅需改动这一处 - 支持任意数量的参数:不管路由有多少个占位符都可以自动填充
- 调用更简洁:不需要把参数包装成数组传入,直接按顺序传参即可
内容的提问来源于stack exchange,提问作者kevin
相关产品推荐
相关产品推荐

