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

TypeScript严格模式下动态访问不同对象属性的类型标注方法

问题描述

现有多类结构不同的对象,所有对象都通过type字段标记自身类型,且不同类型对象使用不同的属性键存储同类型数组:例如下方代码中类型为a的对象将只读数字数组存在childrenA键下,类型为b的对象将只读数字数组存在childrenB键下。
额外定义了HIERARCHY常量映射type值和对应数组的存储键,需要实现getChildren函数,传入对象返回对应存储的数组,要求在TypeScript严格模式下无类型报错。
初始实现代码如下:

const a = {
    type: "a",
    childrenA: [ 1, 2, 3 ]
} as const;

const b = {
    type: "b",
    childrenB: [ 4, 5, 6 ]
} as const;

const HIERARCHY = {
    a: "childrenA",
    b: "childrenB",
} as const;

const getChildren = <T extends typeof a | typeof b>(obj: T) => {
    const ret = obj[HIERARCHY[obj.type]];
    return ret;
};

const AOrB = Math.random() > 0.5 ? a : b;
getChildren(AOrB);

已定位到类型报错的核心原因:

  • 当入参是a | b联合类型时,obj.type的推导类型为"a" | "b"
  • 对应HIERARCHY[obj.type]的推导类型为"childrenA" | "childrenB"
  • TypeScript不会跟踪两个联合类型值的对应匹配关系,会默认认为存在「用childrenB键访问a类型对象」「用childrenA键访问b类型对象」的非法可能,因此抛出类型错误,无法通过严格模式校验。
实现方案

这个是TypeScript中典型的关联记录类型校验问题,TS本身不会自动跨表达式跟踪联合类型的对应关系,我们可以通过泛型+类型映射的方式明确类型对应规则,仅需要极小范围的安全类型断言即可通过严格模式检查,不需要使用any或者@ts-ignore绕过校验。
修正后的完整代码:

const a = {
    type: "a",
    childrenA: [ 1, 2, 3 ]
} as const;

const b = {
    type: "b",
    childrenB: [ 4, 5, 6 ]
} as const;

const HIERARCHY = {
    a: "childrenA",
    b: "childrenB",
} as const;

// 定义所有支持的对象类型联合
type SupportObj = typeof a | typeof b;
// 从HIERARCHY常量推导type到存储键的类型映射
type TypeToKeyMap = typeof HIERARCHY;

/**
 * 传入对象获取对应存储的子数组
 */
const getChildren = <T extends SupportObj>(obj: T): T[TypeToKeyMap[T['type']]] => {
    // 此处断言为安全断言:HIERARCHY为只读常量,键值映射与对象type严格对应,不存在非法访问
    return obj[HIERARCHY[obj.type]] as T[TypeToKeyMap[T['type']]];
};

// 测试:传入联合类型对象时,返回值会被正确推导为 readonly [1,2,3] | readonly [4,5,6]
const AOrB = Math.random() > 0.5 ? a : b;
const children = getChildren(AOrB);

关键逻辑说明:

  • 用泛型T约束入参类型,而非直接将参数标注为联合类型,TS可以基于传入的具体参数类型推导对应返回值,不会丢失类型关联
  • 返回值类型T[TypeToKeyMap[T['type']]]完全对齐运行时逻辑:先取入参的type值,映射到对应存储键,再取该键在入参对象上的属性类型
  • 函数内部的类型断言是可控的:因为HIERARCHY是只读常量,映射关系在定义后不会被修改,运行时不会出现键和对象类型不匹配的情况,断言仅用于告知TS我们已经保证了这层对应关系,没有破坏类型安全。

后续如果新增对象类型,只需要在定义新对象后,将其加入SupportObj联合类型、在HIERARCHY中补充对应type和存储键的映射即可,getChildren函数不需要任何修改,类型会自动适配。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 14:51:19