如何让TypeScript正确理解泛型对象的赋值操作?
解决TypeScript泛型索引访问的类型兼容性问题(无需类型断言)
原代码与问题描述
type Type = 'one' | 'two' | 'three' type TypeSelection = 'one' | 'two' interface InnerObject<T extends Type> { name: string, type: T } type Obj<K extends Type> = { [P in K]?: InnerObject<P> } & { [P in TypeSelection]: InnerObject<P> } const obj: Obj<Type> = { one: { name: 'a', type: 'one' }, two: { name: 'a', type: 'two' } } function getInnerObject<T extends Type>(key: T) { const selectedObj: InnerObject<T> | undefined = obj[key] const defaultObj = obj['one'] }
这段代码中,我们期望obj[key]返回InnerObject<T> | undefined(因为T被约束为Type,key是T类型),但TypeScript会抛出如下错误:
Type 'InnerObject<"one"> | InnerObject<"two"> | InnerObject<"three"> | undefined' is not assignable to type 'InnerObject<T> | undefined'. Type 'InnerObject<"one">' is not assignable to type 'InnerObject<T>'. Type '"one"' is not assignable to type 'T'. '"one"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'Type'.(2322)
问题根源在于原Obj<K>类型采用了两个映射类型的交叉,TypeScript无法将这种交叉类型的索引访问结果与泛型T建立精确对应关系,只能推断出所有可能类型的联合,进而认为联合类型中的某些成员无法赋值给InnerObject<T>。
解决方案:调整Obj类型定义为明确的映射结构
将Obj的类型定义改为单一的映射结构(或合并后的映射类型),让TypeScript能清晰识别每个key对应的value类型,从而正确推断泛型索引访问的结果:
type Type = 'one' | 'two' | 'three' type TypeSelection = 'one' | 'two' interface InnerObject<T extends Type> { name: string; type: T; } // 定义Obj类型:TypeSelection的属性必填,其余Type成员可选 type Obj = { [P in TypeSelection]: InnerObject<P>; } & Partial<{ [P in Exclude<Type, TypeSelection>]: InnerObject<P>; }>; // 或者更直观的写法(适合固定枚举值的场景): // type Obj = { // one: InnerObject<'one'>; // two: InnerObject<'two'>; // three?: InnerObject<'three'>; // }; const obj: Obj = { one: { name: 'a', type: 'one' }, two: { name: 'a', type: 'two' } }; function getInnerObject<T extends keyof Obj>(key: T) { // TypeScript能正确推断obj[key]的类型为Obj[T],无需额外断言 const selectedObj = obj[key]; const defaultObj = obj['one']; // 类型自动推断为InnerObject<'one'> }
原理说明
调整后的Obj类型明确了每个key对应的value类型:
- 对于
TypeSelection中的'one'和'two',属性是必填的InnerObject<'one'>和InnerObject<'two'> - 对于
Type中剩下的'three',属性是可选的InnerObject<'three'>
当函数使用T extends keyof Obj作为泛型约束时,TypeScript能准确将obj[key]的类型与T关联,自动推断出obj[key]的类型为Obj[T],完美匹配我们期望的InnerObject<T>(或InnerObject<T> | undefined),从而避免类型错误。
内容的提问来源于stack exchange,提问作者Magnus
相关产品推荐
相关产品推荐

