You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 19:13:15