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

如何确保TypeScript映射类型的键值一致并支持递归嵌套?

解决TypeScript映射类型的严格约束与递归嵌套问题

一、基础问题:严格约束键与值完全相同

你最初定义的{ [Key in string]: Key }之所以无法约束键值一致,是因为TypeScript的结构类型检查会将字符串字面量视为string的子类型——只要值是string类型就会通过检查,而非严格匹配键的字面量。

要实现键与值完全相同的约束,需要让泛型T满足「每个键K的值必须等于K本身的字面量类型」,可以通过泛型自引用约束实现:

function mapped<const T extends { [K in keyof T]: K }>(t: T): T {
    return t;
}

// 合法:值与键严格匹配
const mapped1 = mapped({
    'foo': 'foo',
});

// 报错:类型 '"bar"' 不能赋值给类型 '"foo"'
const mapped2 = mapped({
    'foo': 'bar',
});

二、递归嵌套结构的类型约束

针对你需要的递归嵌套结构,我们可以定义一个递归索引类型,确保每个层级的fn参数自动绑定当前键的字面量类型,同时children复用同类型实现无限嵌套:

type RecursiveMapped = {
    [Key in string]: {
        fn: (key: Key) => void;
        children: RecursiveMapped;
    };
};

如果需要严格约束传入对象的结构,同时自动推断每个fn的参数类型,可以结合泛型函数使用:

function createMapped<const T extends RecursiveMapped>(t: T): T {
    return t;
}

// 合法:每个fn的参数自动推断为对应键的字面量类型
const validTree = createMapped({
    'foo': {
        fn: (key) => {
            // key 类型为 'foo'
            console.log(key);
        },
        children: {
            'bar': {
                fn: (key) => {
                    // key 类型为 'bar'
                    console.log(key);
                },
                children: {}
            }
        }
    }
});

// 报错:fn的参数类型与键不匹配
const invalidTree = createMapped({
    'foo': {
        fn: (key: 'bar') => {}, // 类型 '"bar"' 不能赋值给类型 '"foo"'
        children: {}
    }
});

若需要禁止对象包含额外属性,可以自定义Exact类型增强约束(注意递归场景下可能存在性能损耗,按需使用):

type Exact<T, U> = T extends U ? U extends T ? T : never : never;

function createMapped<const T extends Exact<T, RecursiveMapped>>(t: T): T {
    return t;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 19:57:45