TypeScript中如何定义基于键约束值的映射类型?
问题分析与解决
问题原因
你定义的Mapping类型使用了[T in string]这种字符串索引签名,但TypeScript处理这类索引签名时,会将每个属性的类型统一推断为Wrapper<string>——因为string是所有字符串字面量的超类型,Wrapper<T>里的T会被自动拓宽为string,导致键和值的类型约束关系完全丢失。所以无论你给键"a"赋值什么字符串类型的value,TypeScript都会认为是合法的,不会触发编译错误。
正确实现方式
要让键K和对应的value类型K严格绑定,需要用泛型约束整个映射对象,而非字符串索引签名。以下是几种可行的写法:
1. 泛型Mapping类型
定义一个接受键集合泛型参数的Mapping类型:
type Wrapper<T extends string> = { value: T } // T是允许的键的集合,每个键K对应Wrapper<K> type Mapping<T extends string> = { [K in T]: Wrapper<K> }
使用时显式指定键的类型,此时赋值错误的value会触发编译报错:
// 报错:类型"b"不能赋值给类型"a" const mapping: Mapping<"a"> = { "a": { value: "b" }, }
2. 用satisfies结合const断言
如果不想显式指定泛型,可利用satisfies运算符让TypeScript自动推导键的类型并做约束:
type Wrapper<T extends string> = { value: T } // 报错:类型"b"不能赋值给类型"a" const mapping = { "a": { value: "b" }, } as const satisfies { [K in keyof typeof mapping]: Wrapper<K> }
3. 泛型工厂函数
最灵活的方式是用泛型函数创建映射,自动推导键的类型并约束value:
type Wrapper<T extends string> = { value: T } function createMapping<T extends string>(obj: { [K in T]: Wrapper<K> }) { return obj; } // 报错:类型"b"不能赋值给类型"a" const mapping = createMapping({ "a": { value: "b" }, });
内容的提问来源于stack exchange,提问作者Adam A
相关产品推荐
相关产品推荐

