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

能否声明带类型的关联数组并将其用作键类型化对象?

实现键类型化的递归关联数组(TypeScript)

问题背景

你定义了如下递归关联数组类型,但使用时无法通过具体键(如key1、subKey1)获取属性——因为AssociativeArrayT使用了字符串索引签名,TypeScript无法推断对象的具体键名,导致类型提示丢失:

type CellT = { name: string; value: number };
type AssociativeArrayT = {
    [key: string]: CellT[] | AssociativeArrayT;
};

const myObject: AssociativeArrayT = {
    key1: {
        subKey1: [
            { name: 'SomeName1', value: 100 },
            { name: 'SomeName2', value: 100 },
        ],
    },
    key2: [
        { name: 'SomeName3', value: 300 },
        { name: 'SomeName4', value: 400 },
    ],
};

需求是:既能用类型校验对象结构(值只能是CellT[]或子关联数组),又能保留对象的具体键类型,支持通过键直接访问属性。

解决方案

可以通过泛型递归类型实现,以下提供两种实用方案:

方案1:提前定义结构类型(适合明确约束场景)

通过泛型递归类型约束结构,同时保留具体键的类型信息:

type CellT = { name: string; value: number };

// 泛型递归类型:约束每个键的值要么是CellT数组,要么是子关联数组
type AssociativeArray<T extends Record<string, any>> = {
  [K in keyof T]: 
    T[K] extends CellT[] ? CellT[] : 
    T[K] extends Record<string, any> ? AssociativeArray<T[K]> : 
    never;
};

// 定义具体的对象结构类型
type MyObjectSchema = AssociativeArray<{
  key1: {
    subKey1: CellT[];
  };
  key2: CellT[];
}>;

// 初始化对象,既受结构校验,又保留键类型
const myObject: MyObjectSchema = {
  key1: {
    subKey1: [
      { name: 'SomeName1', value: 100 },
      { name: 'SomeName2', value: 100 },
    ],
  },
  key2: [
    { name: 'SomeName3', value: 300 },
    { name: 'SomeName4', value: 400 },
  ],
};

// 正常访问具体键,类型提示完整
console.log(myObject.key1.subKey1[0].value); // 类型推断为number

方案2:自动推断字面量类型(灵活初始化场景)

用工具函数结合递归约束类型,直接从对象字面量推断键类型,同时校验结构:

type CellT = { name: string; value: number };

// 递归约束:定义合法的关联数组结构
type ValidAssociativeArray = 
  | CellT[] 
  | { [key: string]: ValidAssociativeArray };

// 工具函数:接收符合约束的对象,返回其字面量类型
const createAssociativeArray = <T extends ValidAssociativeArray>(obj: T) => obj;

// 初始化时调用工具函数,自动推断键类型并校验结构
const myObject = createAssociativeArray({
  key1: {
    subKey1: [
      { name: 'SomeName1', value: 100 },
      { name: 'SomeName2', value: 100 },
    ],
  },
  key2: [
    { name: 'SomeName3', value: 300 },
    { name: 'SomeName4', value: 400 },
  ],
});

// 支持直接访问具体键,类型提示准确
myObject.key1.subKey1; // 类型为CellT[]
myObject.key2; // 类型为CellT[]

方案对比

  • 方案1:需提前定义结构类型,适合需要明确对外暴露接口的场景,结构约束更严格。
  • 方案2:无需提前定义结构,直接从字面量推断,初始化更灵活,适合快速开发场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 09:33:27