如何在TypeScript中用联合变量访问属性并维持键值类型关联?
解决TypeScript联合类型索引访问的类型关联问题
问题分析
你遇到的问题是TypeScript无法维持FactoryMapping中键(ActionType)与值(Encoder<T>)的类型关联。当通过factories[type]获取编码器时,TS会将其推断为所有Encoder<T>的联合类型,而action是Action联合类型,此时调用factory(action)会要求action满足所有Encoder参数的交集——也就是never类型,因为{type: 'withdraw', address: string}和{type: 'deposit', address: number}没有共同属性交集。
解决方案
方法1:用泛型函数封装类型关联
通过泛型函数提取对应type的Action分支,明确编码器与动作的类型绑定:
type ActionType = 'withdraw' | 'deposit' type Action = { type: 'withdraw'; address: string } | { type: 'deposit' address: number } type Encoder<T extends ActionType> = (action: Extract<Action, { type: T }>) => string type FactoryMapping = { [key in ActionType]?: Encoder<key> } const factories: FactoryMapping = { withdraw: action => action.address, deposit: action => action.address.toFixed(2), } const action: Action = JSON.parse( process.env['ACTION'] || '', ) as Action // 泛型函数建立类型关联 function executeAction<T extends ActionType>(action: Extract<Action, { type: T }>) { const factory = factories[action.type] as Encoder<T>; if (factory) { return factory(action); } } executeAction(action);
方法2:让TypeScript自动推断精确类型
去掉手动指定的FactoryMapping,用as const让TS保留每个键值对的精确类型,再通过分支判断明确类型:
type ActionType = 'withdraw' | 'deposit' type Action = { type: 'withdraw'; address: string } | { type: 'deposit' address: number } // 让TS自动推断factories的类型,as const确保类型不被拓宽 const factories = { withdraw: (action: Extract<Action, { type: 'withdraw' }>) => action.address, deposit: (action: Extract<Action, { type: 'deposit' }>) => action.address.toFixed(2), } as const; const action: Action = JSON.parse( process.env['ACTION'] || '', ) as Action // 通过switch分支明确类型关联 switch (action.type) { case 'withdraw': factories.withdraw(action); break; case 'deposit': factories.deposit(action); break; }
方法3:重构类型定义,强化关联
重新定义FactoryMapping为更精确的类型,确保每个键对应的编码器参数与动作严格匹配:
type ActionType = 'withdraw' | 'deposit' type Action = { type: 'withdraw'; address: string } | { type: 'deposit' address: number } // 为每个ActionType定义对应的Action分支 type ActionByType<T extends ActionType> = Extract<Action, { type: T }> // 定义精确的工厂映射类型 type FactoryMapping = { [T in ActionType]?: (action: ActionByType<T>) => string } const factories: FactoryMapping = { withdraw: action => action.address, deposit: action => action.address.toFixed(2), } const action: Action = JSON.parse( process.env['ACTION'] || '', ) as Action // 用泛型工具函数处理调用 function runFactory<T extends ActionType>(type: T, action: ActionByType<T>) { const factory = factories[type]; if (factory) { factory(action); } } // 这里需要用类型断言让TS识别action的分支类型 runFactory(action.type, action as ActionByType<typeof action.type>);
关键原理
TypeScript在处理联合类型的索引访问时,默认会丢失分支间的类型绑定。通过泛型函数、分支判断(switch/if)或者精确的类型推断,可以强制TS维持键与值的类型关联,避免将action推断为never类型。
内容的提问来源于stack exchange,提问作者Arman
相关产品推荐
相关产品推荐

