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

为何TypeScript在相似动态属性赋值中类型推断表现不同?

TypeScript动态赋值对象时的类型推断差异问题

问题描述

使用TypeScript 5.7.3(默认tsconfig.json配置)时,两段逻辑相似的对象动态赋值代码表现不一致:

  • 函数内部的动态赋值代码正常运行,无类型错误
  • 顶层作用域的动态赋值代码直接报错:Property 'd' does not exist on type '{}'

正常运行代码

interface Student {
  id: number;
  name: string;
  college: string;
  marks: number;
}

interface Group {
  College: string;
  noOfStudent: number;
}

const groupByCollege = (arr: Student[]): Group[] => {
  const result: Group[] = [];
  const obj = {};
  for (const el of arr) {
    if (obj[el.college] === undefined) {
      obj[el.college] = 1;
    } else {
      obj[el.college] += 1;
    }
  }

  for (const key in obj) {
    const singleCollege: Group = {
      College: key,
      noOfStudent: obj[key],
    };
    result.push(singleCollege);
  }
  return result;
};

报错代码

const obj = {};

obj["d"] = 1; // Error: Property 'd' does not exist on type '{}'

原因分析

这是TypeScript类型推断的上下文差异导致的:

  • 函数内部的变量:当你在函数里声明const obj = {}后,在for...of循环中对obj[el.college]进行赋值时,TypeScript的控制流分析会自动拓宽obj的类型——因为el.college是明确的string类型,TS会将obj的类型更新为{ [key: string]: number | undefined },允许动态添加字符串键对应的数值属性,所以不会报错。
  • 顶层作用域的变量:在全局或模块顶层声明的const obj = {},TS会严格推断其类型为{}(空对象类型,不允许添加任何新属性),因为顶层变量的类型推断默认更保守,不会自动进行类型拓宽,所以直接赋值新属性会触发类型错误。

解决方案

针对顶层作用域的动态赋值场景,有几种解决方式:

1. 显式声明对象类型(推荐)

直接给obj指定支持动态键值的类型,比如使用Record工具类型:

const obj: Record<string, number> = {};
obj["d"] = 1; // 正常运行

或者手动定义索引签名:

const obj: { [key: string]: number } = {};
obj["d"] = 1; // 正常运行

2. 使用类型断言

如果只是临时需要绕过类型检查,可以用类型断言:

const obj = {};
(obj as Record<string, number>)["d"] = 1; // 正常运行

3. 使用any类型(不推荐)

虽然可以解决问题,但会丢失类型检查的优势,仅在特殊场景下使用:

const obj: any = {};
obj["d"] = 1; // 正常运行

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 15:14:51