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

TypeScript动态访问设置对象的类型安全解决方案(v4.9.5)

TypeScript 动态访问/设置对象属性的类型安全解决方案(4.9.5版本)

问题场景

当你尝试通过keyof类型的参数动态重置对象属性时,TypeScript会将赋值两边的类型识别为联合类型(比如PriceCorrections | Record<number, PriceCorrection>或boolean | Record<number, boolean>),即使逻辑上是同一个键、类型完全匹配,也会触发赋值错误。冗余的分支判断虽然能解决问题,但代码不够简洁。

用简化代码复现问题:

export type ObjectType = {
  main: boolean;
  services: Record<number, boolean>;
};

let editedState: ObjectType = {
  main: false,
  services: {},
};

let initialState: ObjectType = {
  main: false,
  services: {},
};

// 报错:类型 'boolean | Record<number, boolean>' 不能赋值给类型 'boolean & Record<number, boolean>'
const resetStateBroken = (action: keyof ObjectType) => (editedState[action] = initialState[action]);

// 冗余分支才能正常工作
const resetStateWorks = (action: keyof ObjectType) => {
  if (action === 'services') {
    editedState[action] = initialState[action];
  } else {
    editedState[action] = initialState[action];
  }
};

类型安全的解决方案

1. 泛型函数(最简洁直接)

通过泛型捕获具体的键类型,让TypeScript明确追踪赋值两边的类型匹配关系:

const resetStateGeneric = <K extends keyof ObjectType>(action: K) => {
  editedState[action] = initialState[action];
};

泛型K会绑定传入的具体键(比如'main'或'services'),TypeScript能确定editedState[K]和initialState[K]的类型完全一致,不会再将它们视为宽泛的联合类型。

2. 适配原始PriceCorrections类型的写法

针对你定义的PriceCorrections类型,同样可以用泛型实现类型安全的重置:

export type PriceCorrection = { /* 补充你的PriceCorrection类型定义 */ };
export type PriceCorrections = {
  main: PriceCorrections;
  services: Record<number, PriceCorrection>;
};

const initialState: PriceCorrections = { 
  main: { /* 递归初始化 */ },
  services: {}
};
let state = { priceCorrections: initialState };

const resetPriceCorrection = <K extends keyof PriceCorrections>(key: K) => {
  state.priceCorrections[key] = initialState[key];
};

3. 类型守卫(适用于更复杂的判断场景)

如果后续需要扩展更多键,不想写冗余分支,可以用类型守卫明确键的类型:

function isSpecificKey<K extends keyof ObjectType>(key: keyof ObjectType): key is K {
  return ['main', 'services'].includes(key as string);
}

const resetStateWithGuard = (action: keyof ObjectType) => {
  if (isSpecificKey<'main'>(action)) {
    editedState[action] = initialState[action];
  } else if (isSpecificKey<'services'>(action)) {
    editedState[action] = initialState[action];
  }
};

不过这种方式不如泛型简洁,仅推荐在有额外逻辑判断的场景使用。

原理说明

原始代码报错的原因是:action的类型是keyof ObjectType(即联合类型'main' | 'services'),TypeScript的类型系统默认会假设存在editedState['main'] = initialState['services']这种类型不匹配的赋值可能,因此拒绝通过编译。而泛型通过将键的类型绑定为具体的K,消除了这种不确定性,让类型系统确认赋值两边类型完全一致。

内容的提问来源于stack exchange,提问作者Romasik

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 19:13:17