TypeScript条件类型实现回调存储时类型交集错误排查
TypeScript 类型报错:Callback 无法赋值给 Callback<"ok"> & Callback<"error">
我来帮你拆解这个问题:
问题背景
你想要通过泛型和条件类型,把对应类型的回调分别存入storage.ok(接收Record<string, string>参数)和storage.error(接收Error参数)集合,但在调用storage[type].add(callback)时遇到了类型报错,核心疑问是:为什么add方法的参数会被推断成Callback<"ok"> & Callback<"error">这个交集类型?
交集类型的来源分析
这是TypeScript处理联合类型索引访问时的特性:
- 当你用泛型
T extends Mapping['type']作为索引访问storage[type]时,TypeScript没办法在编译期确定type到底是"ok"还是"error",所以它会把storage[type]推断成联合类型:Set<Callback<"ok">> | Set<Callback<"error">>。 - 对于联合类型的方法调用,TypeScript会要求参数满足所有联合成员方法的参数要求(也就是取参数类型的交集)——因为只有这样,不管联合类型实际是哪一个成员,调用方法都是安全的。所以
add方法的参数就变成了Callback<"ok"> & Callback<"error">,而你的Callback<T>只满足其中一种类型的要求,自然会报错。
解决方案
这里有几种可行的修复方式,按需选择:
1. 类型断言(简单直接)
直接告诉TypeScript,storage[type]就是对应Set<Callback<T>>类型:
const storage: Store = { ok: new Set(), error: new Set(), }; function store<T extends Mapping['type']>( type: T, callback: Callback<T> ): void { (storage[type] as Set<Callback<T>>).add(callback); }
2. 条件分支缩小类型(更安全)
通过明确的条件判断,让TypeScript精确推断出当前操作的集合类型:
const storage: Store = { ok: new Set(), error: new Set(), }; function store<T extends Mapping['type']>( type: T, callback: Callback<T> ): void { if (type === 'ok') { storage.ok.add(callback as Callback<"ok">); } else { storage.error.add(callback as Callback<"error">); } }
3. 函数重载(最优雅的调用体验)
用函数重载明确每种调用场景的类型,让TypeScript在编译期就能精确匹配:
const storage: Store = { ok: new Set(), error: new Set(), }; // 重载声明 function store(type: 'ok', callback: Callback<'ok'>): void; function store(type: 'error', callback: Callback<'error'>): void; // 实现函数 function store(type: Mapping['type'], callback: Callback<any>): void { storage[type].add(callback); }
这样调用store('ok', (data) => {})时,TypeScript会自动推断data是Record<string, string>,调用store('error', (err) => {})时推断err是Error,完全符合你的需求。
内容的提问来源于stack exchange,提问作者Zauberbutter
相关产品推荐
相关产品推荐

