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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 17:18:11