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

为何在TypeScript中设置动态键存在类型不安全问题?

TypeScript索引类型安全漏洞解析与规避

问题复现

以下代码在TypeScript严格模式下可通过类型检查,但实际存在类型不安全问题——数字被赋值给了声明为字符串类型的属性a:

type Example = {
    a: string,
    b: number
}

let value: Example = {
    a: 'hello',
    b: 10,
}

// 立即执行函数绕过TypeScript类型收窄,让k保持为keyof Example联合类型
const k = ((): keyof Example => 'a')()

value = {
    ...value,
    // 此处将数字5赋值给string类型的a,但TypeScript未报错
    [k]: 5,
}

根本原因

这个问题的核心是TypeScript对联合类型索引访问的设计限制:

  • 当使用keyof T类型的变量作为索引时,TypeScript不会跟踪变量对应的具体字面量类型(比如这里的k实际值是'a',但类型被推断为'a' | 'b')。
  • 此时TypeScript会将索引赋值的类型检查放宽为:只要赋值的类型是T[keyof T](即所有属性类型的联合)的子集,就允许通过。这里T[keyof Example]是string | number,5属于这个联合,因此不会触发错误。
  • 立即执行函数的作用是规避TypeScript的类型收窄——如果直接赋值const k: keyof Example = 'a',TypeScript会把k的类型收窄为'a',此时赋值[k]:5会直接报错,但用IIFE绕过了这个逻辑。

规避方案

针对这类问题,可通过以下几种方式确保类型安全:

1. 使用泛型函数关联键与值的类型

通过泛型强制索引键和对应值的类型匹配,从根源上避免不匹配的赋值:

type Example = { a: string; b: number };

let value: Example = { a: 'hello', b: 10 };
const k = ((): keyof Example => 'a')();

// 泛型函数确保key对应的value类型严格匹配
function update<T, K extends keyof T>(obj: T, key: K, value: T[K]): T {
  return { ...obj, [key]: value };
}

// 此处会触发正确的类型错误:类型"number"不能赋值给类型"string"
value = update(value, k, 5);

2. 避免使用宽类型的索引变量

尽量保持索引的字面量类型,不要用keyof T这类联合类型。如果必须使用联合类型,可通过类型守卫缩小范围:

if (k === 'a') {
  // 此时k的类型被收窄为'a',赋值数字会报错
  value[k] = 5; // 类型错误:number不能赋值给string
} else {
  value[k] = 5; // 正确,因为k此时是'b',对应number类型
}

3. 直接修改属性而非扩展语法+索引

如果明确知道要修改的属性,直接通过属性访问符修改,TypeScript会严格检查类型:

// 直接修改a属性会立即报错
value.a = 5; // 类型错误:number不能赋值给string

官方参考

  • GitHub议题:#13948(讨论联合类型索引访问时的类型检查行为)
  • TypeScript官方文档:《索引类型》章节(说明keyof和索引访问类型的行为限制)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 04:27:21