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

TypeScript多条件函数返回类型报错及多条件场景优化咨询

TypeScript泛型条件类型返回值错误与多分支处理方案

问题描述

我想实现一个根据参数返回不同类型的函数,用泛型实现且避免返回联合类型,预期结构类似:

type ResultType = {
  get: GetResult
  post: PostResult
  // 其他操作类型
}

function fetch(operation: keyof ResultType): GetResult | PostResult | ...

参考TypeScript官方手册的条件类型示例写了如下代码:

interface IdLabel {
  id: number /* 其他字段 */
}
interface NameLabel {
  name: string /* 其他字段 */
}

type NameOrId<T extends number | string> = T extends number ? IdLabel : NameLabel

function createLabel<T extends number | string>(idOrName: T): NameOrId<T> {
  if (typeof idOrName === 'number') {
    return { id: idOrName }
  } else {
    return { name: idOrName }
  }
}

但出现类型错误:

Type '{ id: number; }' is not assignable to type 'NameOrId<T>'.
Type '{ name: string; }' is not assignable to type 'NameOrId<T>'.

想请教:1. 这段代码哪里出错了?2. TypeScript里有没有其他方式处理大量条件分支(类似switch的场景)?


一、代码错误原因及修复

TypeScript编译器在函数内部无法精确推断泛型T的具体类型——虽然你用typeof idOrName === 'number'做了运行时判断,但编译器不知道T到底是number还是string,因此无法确认返回的具体对象一定符合NameOrId<T>的类型约束。

修复方案

方案1:使用类型断言

明确告诉编译器当前返回值的类型符合泛型条件:

function createLabel<T extends number | string>(idOrName: T): NameOrId<T> {
  if (typeof idOrName === 'number') {
    return { id: idOrName } as NameOrId<T>
  } else {
    return { name: idOrName } as NameOrId<T>
  }
}

方案2:使用函数重载

重载能更清晰地定义参数与返回值的对应关系,比条件类型更直观:

function createLabel(idOrName: number): IdLabel;
function createLabel(idOrName: string): NameLabel;
function createLabel(idOrName: number | string): IdLabel | NameLabel {
  if (typeof idOrName === 'number') {
    return { id: idOrName }
  } else {
    return { name: idOrName }
  }
}

二、处理大量条件分支的方案

当分支数量较多时,推荐用映射类型+对象映射的方式替代switch/if链,既保证类型安全,又便于维护:

步骤1:定义操作与类型的映射

// 操作类型与返回类型的映射
type OperationMap = {
  get: GetResult;
  post: PostResult;
  put: PutResult;
  delete: DeleteResult;
};

// 提取所有操作类型
type Operation = keyof OperationMap;

步骤2:创建操作处理的对象映射

把每个操作的逻辑封装成函数,用对象存储:

const handlers: { [K in Operation]: () => OperationMap[K] } = {
  get: () => ({ data: 'get data' } as GetResult),
  post: () => ({ status: 201 } as PostResult),
  put: () => ({ updated: true } as PutResult),
  delete: () => ({ deletedId: 123 } as DeleteResult),
};

步骤3:实现主函数

利用泛型约束和索引访问类型,让返回值自动匹配操作类型:

function fetch<K extends Operation>(operation: K): OperationMap[K] {
  return handlers[operation]();
}

// 使用示例
const getRes = fetch('get'); // 类型为GetResult
const postRes = fetch('post'); // 类型为PostResult

这种方式的优势是:新增操作时只需修改OperationMap和handlers,无需修改主函数逻辑,同时TypeScript会自动校验操作与返回值的类型一致性。


内容的提问来源于stack exchange,提问作者Julien Lalaut

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 18:12:53