You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何编写带类型的函数统一管理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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 14:21:02