TypeScript:基于输入类型推导返回类型的实现问题
解决TypeScript中getHandler函数的类型关联问题
问题重现
先给出基础类型定义和错误的实现,以及触发报错的调用场景:
// 基础类型定义 type FooType = { kind: "Foo"; fooProp: string }; type BarType = { kind: "Bar"; barProp: number }; type FooBarType = FooType | BarType; // 错误的getHandler实现 function getHandler(kind: "Foo" | "Bar") { switch (kind) { case "Foo": return { handle: (item: FooType) => console.log(item.fooProp) }; case "Bar": return { handle: (item: BarType) => console.log(item.barProp) }; } } // 调用时触发TS2345错误 const fooBar: FooBarType = Math.random() > 0.5 ? { kind: "Foo", fooProp: "test" } : { kind: "Bar", barProp: 123 }; const handler = getHandler(fooBar.kind); handler.handle(fooBar); // 错误:类型'FooType | BarType'无法赋值给类型'never'
错误原因
TypeScript无法自动关联handler的类型与fooBar的类型:原函数返回的是联合类型{handle: (item: FooType) => void} | {handle: (item: BarType) => void},调用handle时,TS会取两个分支参数类型的交集(FooType & BarType),而这两个类型没有共同属性,交集结果为never,因此触发报错。
解决方案:用泛型绑定Kind与对应类型
通过泛型让TypeScript追踪kind参数和返回值的类型关联,具体有两种实现方式:
方式1:使用类型映射表(可读性更高)
先定义kind到对应类型的映射,再通过泛型约束实现类型关联:
type FooType = { kind: "Foo"; fooProp: string }; type BarType = { kind: "Bar"; barProp: number }; type FooBarType = FooType | BarType; // 定义kind与对应类型的映射表 type KindMap = { "Foo": FooType; "Bar": BarType; }; // 泛型版getHandler function getHandler<K extends keyof KindMap>(kind: K) { switch (kind) { case "Foo": return { handle: (item: KindMap[K]) => console.log(item.fooProp) } as const; case "Bar": return { handle: (item: KindMap[K]) => console.log(item.barProp) } as const; default: throw new Error(`Unknown kind: ${kind}`); } } // 调用示例:无类型错误 const fooBar: FooBarType = Math.random() > 0.5 ? { kind: "Foo", fooProp: "test" } : { kind: "Bar", barProp: 123 }; const handler = getHandler(fooBar.kind); handler.handle(fooBar);
方式2:使用条件类型(无需额外映射表)
直接通过条件类型根据kind推导对应参数类型:
type FooType = { kind: "Foo"; fooProp: string }; type BarType = { kind: "Bar"; barProp: number }; type FooBarType = FooType | BarType; function getHandler<K extends FooBarType["kind"]>(kind: K) { // 根据kind推导对应的参数类型 type ItemType = K extends "Foo" ? FooType : BarType; switch (kind) { case "Foo": return { handle: (item: ItemType) => console.log(item.fooProp) }; case "Bar": return { handle: (item: ItemType) => console.log(item.barProp) }; default: throw new Error(`Unknown kind: ${kind}`); } } // 调用示例:无类型错误 const fooBar: FooBarType = Math.random() > 0.5 ? { kind: "Foo", fooProp: "test" } : { kind: "Bar", barProp: 123 }; const handler = getHandler(fooBar.kind); handler.handle(fooBar);
原理说明
泛型K会被推断为fooBar.kind的具体值("Foo"或"Bar"),对应的KindMap[K]或ItemType会被解析为匹配的类型(FooType或BarType)。TypeScript此时能识别出fooBar的类型与handle的参数类型完全匹配,因此不会再抛出错误。
内容的提问来源于stack exchange,提问作者asemahle
相关产品推荐
相关产品推荐

