TypeScript中含不同类型联合参数的函数查找对象问题及解法
问题分析与解决方案
你的判断部分正确:直接创建处理器对象后动态调用确实会触发类型冲突,但并非必须用类型断言绕过TypeScript,完全可以通过类型编程实现类型安全的动态调用。
为什么会出现never类型?
当你通过动态键(比如processorKeyArgument)访问Processors对象时,TypeScript会将processor的类型推断为所有处理器函数的交集类型:
ProcessFunction<"monkey" | "dog"> & ProcessFunction<"cat" | "dinosaur">
而函数交集的参数类型是各函数参数的交集,也就是:
{ type: "monkey" | "dog" } & { type: "cat" | "dinosaur" }
由于这两个type的取值集合完全互斥,交集结果就是never,导致无法正常传入参数。
类型安全的解决方案
方案1:建立处理器与类型的强关联映射
重新设计处理器对象,让每个处理器的键和它支持的type值建立明确关联,通过类型编程让TypeScript自动推导正确的参数类型:
type ProcessFunction<U> = (options: { type: U }) => void; // 定义每个处理器对应的type类型 type ProcessorMap = { createStuff: "monkey" | "dog"; deleteStuff: "cat" | "dinosaur"; updateStuff: "elephant" | "giraffe"; }; // 根据ProcessorMap生成对应的处理器对象类型 type ProcessorObject = { [K in keyof ProcessorMap]: ProcessFunction<ProcessorMap[K]>; }; // 实现处理器对象(TypeScript会自动校验每个函数的类型是否匹配) const processors: ProcessorObject = { createStuff: ({ type }) => { /* 仅接受"monkey"|"dog" */ }, deleteStuff: ({ type }) => { /* 仅接受"cat"|"dinosaur" */ }, updateStuff: ({ type }) => { /* 仅接受"elephant"|"giraffe" */ }, }; // 动态调用的类型安全函数 function runProcessor<K extends keyof ProcessorMap>( key: K, type: ProcessorMap[K] ) { processors[key]({ type }); } // 调用示例:完全类型安全 runProcessor("createStuff", "monkey"); // 合法 runProcessor("deleteStuff", "cat"); // 合法 // runProcessor("createStuff", "cat"); // 类型错误,编译不通过
方案2:用联合类型描述处理器与参数的对应关系
如果不想提前定义ProcessorMap,可以直接用联合类型明确每个处理器能处理的type集合:
type ProcessFunction<U> = (options: { type: U }) => void; // 定义处理器与type的对应关系联合类型 type ProcessorEntry = | { key: "createStuff"; type: "monkey" | "dog" } | { key: "deleteStuff"; type: "cat" | "dinosaur" } | { key: "updateStuff"; type: "elephant" | "giraffe" }; // 处理器对象 const processors = { createStuff: ({ type }: { type: "monkey" | "dog" }) => { /* */ }, deleteStuff: ({ type }: { type: "cat" | "dinosaur" }) => { /* */ }, updateStuff: ({ type }: { type: "elephant" | "giraffe" }) => { /* */ }, }; // 类型安全的调用函数 function runProcessor(entry: ProcessorEntry) { processors[entry.key]({ type: entry.type }); } // 调用示例 runProcessor({ key: "createStuff", type: "dog" }); // 合法 // runProcessor({ key: "createStuff", type: "cat" }); // 类型错误
这两种方案都不需要放弃类型安全,也不用把type设为宽泛的string,能在编译阶段就校验参数的正确性。
内容的提问来源于stack exchange,提问作者Julio García
相关产品推荐
相关产品推荐

