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

如何推断TypeScript中深度嵌套属性的类型?

TypeScript 4.7.4 嵌套属性字面量类型推断问题的解决方法

问题背景

编写接收描述符对象的函数时,遇到TypeScript类型推断不一致的问题:嵌套属性type(约束为联合字面量类型)能被正确推断为具体字面量,但name(约束为string)始终被推断为宽泛的string类型,而非传入的实际字面量值。尝试过as const断言,但会导致元组变为只读,不符合泛型API的使用需求,且环境限制只能使用TypeScript 4.7.4的特性。

原因分析

TypeScript的类型推断逻辑中,当泛型参数的约束是宽泛的原始类型(如string)时,编译器默认不会自动将字面量值缩窄为具体的字面量类型;但如果约束是联合字面量类型(如示例中的ChildType),编译器会优先匹配具体的联合成员,因此type能被正确推断。原代码中ChildDescriptor[]的约束会让编译器默认使用ChildDescriptor<string, ChildType>作为数组元素类型,跳过了name的字面量推断。

解决方案

通过调整泛型约束和类型映射,让编译器先推断出包含具体字面量的元组,再转换为目标ChildDescriptor类型,同时避免只读元组问题。

修改后的完整代码

type ChildType = 'foo' | 'bar' | 'baz';

type ChildDescriptor<
    TName extends string = string,
    TType extends ChildType = ChildType,
> = {
    name: TName;
    type: TType;
};

// 调整ParentDescriptor的TChildren约束,先让编译器推断字面量
type ParentDescriptor<
    TSomeProp extends string,
    TChildren extends readonly { name: string; type: ChildType }[],
> = {
    someProp: TSomeProp;
    children: [...TChildren]; // 可变元组语法,保留元组结构
};

const identity = <
    TSomeProp extends string,
    TChildren extends readonly { name: string; type: ChildType }[],
>(
    descriptor: ParentDescriptor<TSomeProp, TChildren>,
) => 
// 通过类型映射将推断出的字面量转换为ChildDescriptor类型,移除readonly
descriptor as Omit<ParentDescriptor<TSomeProp, TChildren>, 'children'> & {
    children: {
        [K in keyof TChildren]: ChildDescriptor<TChildren[K]['name'], TChildren[K]['type']>
    };
};

const inferredValue = identity({
    someProp: 'I get inferred',
    children: [
        {
            name: 'I don\'t get inferred',
            type: 'foo',
        },
        {
            name: 'I don\'t get inferred either',
            type: 'baz',
        }
    ],
});

const [childA, childB] = inferredValue.children;
// childA.name 类型:"I don't get inferred"
// childB.name 类型:"I don't get inferred either"
// childA.type 类型:"foo",childB.type 类型:"baz"

方案说明

  1. 调整ParentDescriptor的TChildren约束为readonly { name: string; type: ChildType }[],让编译器先捕捉每个子项的name和type的字面量值。
  2. 使用可变元组语法[...TChildren]保留数组的元组结构,而非退化为普通数组。
  3. 通过类型映射将推断出的元组转换为ChildDescriptor类型数组,同时利用Omit移除原有的readonly属性,得到可变数组类型。
  4. 整个过程无需as const断言,避免了只读元组的问题,且完全兼容TypeScript 4.7.4特性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 22:15:33