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
相关产品推荐
相关产品推荐

