TypeScript中判别式联合与Map对象的类型定义问题
问题:判别式联合类型下Map对象的类型定义错误
现有一段使用判别式联合类型的可正常运行TypeScript代码:
type Alpha = { name: "alpha"; alpha: 1} type Beta = { name: "beta"; beta: 2} type Letters = Alpha | Beta const calculateAlpha = (cfg: Alpha): number => { return cfg.alpha } const calculateBeta = (cfg: Beta): number => { return cfg.beta } const CalculatorFactory1 = (cfg: Letters) => { switch (cfg.name) { case "alpha": return () => calculateAlpha(cfg) case "beta": return () => calculateBeta(cfg) default: throw new Error() } }
为提升可配置性,尝试将switch逻辑重构为Map对象,但定义letterMap类型时出现错误:
重构代码:
type LetterMap = Map<Letters["name"], (c: Letters) => number> const letterMap: LetterMap = new Map([ ["alpha", calculateAlpha], ["beta", calculateBeta] ]) const CalculatorFactory2 = (cfg: Letters) => { const func = letterMap.get(cfg.name) return func(cfg) }
错误信息:
Property 'alpha' is missing in type 'Beta' but required in type 'Alpha'.
解决方案:关联Map键与对应参数类型
问题根源
原LetterMap定义要求所有函数都能接收整个Letters联合类型作为参数,但calculateAlpha仅接受Alpha类型、calculateBeta仅接受Beta类型,类型约束不匹配导致报错。我们需要让Map的键与对应函数的参数类型一一绑定。
正确实现方式
方式1:使用映射类型明确键与类型的对应关系
先定义一个映射类型,将每个name值关联到对应的具体类型,再基于此定义Map的类型:
type Alpha = { name: "alpha"; alpha: 1} type Beta = { name: "beta"; beta: 2} type Letters = Alpha | Beta // 定义name到对应类型的映射 type LetterTypeMap = { alpha: Alpha; beta: Beta; }; const calculateAlpha = (cfg: Alpha): number => { return cfg.alpha } const calculateBeta = (cfg: Beta): number => { return cfg.beta } // 定义Map类型:键为LetterTypeMap的key,值为对应类型的处理函数 type LetterMap = Map<keyof LetterTypeMap, (c: Letters) => number>; // 使用类型断言让TypeScript接受合法的键值对 const letterMap = new Map([ ["alpha", calculateAlpha], ["beta", calculateBeta] ] as [keyof LetterTypeMap, (c: Letters) => number][]); const CalculatorFactory2 = (cfg: Letters) => { const func = letterMap.get(cfg.name); if (!func) throw new Error("Invalid config type"); return func(cfg); };
方式2:利用泛型函数实现类型安全调用
如果希望完全保留类型推断,可以用泛型约束让TypeScript自动匹配键与参数类型:
type Alpha = { name: "alpha"; alpha: 1} type Beta = { name: "beta"; beta: 2} type LetterTypeMap = { alpha: Alpha; beta: Beta; }; type Letters = Alpha | Beta const calculateAlpha = (cfg: Alpha): number => cfg.alpha; const calculateBeta = (cfg: Beta): number => cfg.beta; // 使用as const让TypeScript保留键值对的具体类型 const letterMap = new Map([ ["alpha", calculateAlpha], ["beta", calculateBeta] ] as const); // 泛型函数确保传入的cfg与获取的函数类型匹配 const CalculatorFactory2 = <K extends keyof LetterTypeMap>(cfg: LetterTypeMap[K]) => { const func = letterMap.get(K); if (!func) throw new Error("Invalid config type"); return func(cfg); };
说明
两种方式都解决了原类型定义的问题:
- 方式1通过映射类型统一管理键与类型的对应关系,适合需要扩展更多类型的场景
- 方式2利用泛型和
as const实现更精确的类型推断,调用时无需额外类型断言
内容的提问来源于stack exchange,提问作者Sam Broster
相关产品推荐
相关产品推荐

