TypeScript中如何让泛型Handler接受含指定层级类型的对象?
TypeScript泛型约束问题:让Handler接受含特定层级结构的类型
作为之前问题的后续,我编写了如下代码:
interface SubThing { name: string; } interface Thing { subThing: SubThing; // 或UpperThing中其他SubThing类型的属性 } interface UpperThing { thing: Thing } interface ThingMapA { map: { thing: { subThing: { name : 'subThing name' } } }; } interface ThingMapB { map: { thing: { otherThing: { name : 'subThing name' } } }; } class Handler<T extends Record<keyof T, Record<keyof T, Thing>>> {} const handler = new Handler<ThingMapA>(); // 报错 const handler = new Handler<ThingMapB>(); // 报错
我希望Handler能接受任何包含至少一个UpperThing类型属性(且该UpperThing至少包含一个Thing类型属性)的类型,但当前ThingMap无法被识别,会触发报错,请问有什么解决建议?
以下是基于某方案的另一个示例:
interface SubThing { name: string; } interface Thing { subThing: SubThing; } interface UpperThing { thing: Thing } interface ThingMapA { map: { thing: { subThing: { name: 'subThing name' } } }; } interface ThingMapB { map: { otherThing: { subThing: { name: 'subThing name' } } }; } interface ThingMapC { map: { thing: { otherSubThing: { name: 'subThing name' } } }; } interface ThingMapD { map: { otherThing: { otherSubThing: { name: 'subThing name' } } }; } class Handler<T extends { [K in keyof T]: { [P in keyof T[K]]: Thing } }> { } const handlerA = new Handler<ThingMapA>(); // 正常 const handlerB = new Handler<ThingMapB>(); // 正常 const handlerC = new Handler<ThingMapC>(); // 报错 const handlerD = new Handler<ThingMapD>(); // 报错
问题分析
原代码中的泛型约束逻辑不符合需求:
- 初始约束
Record<keyof T, Record<keyof T, Thing>>要求T的每个属性对象的键必须和T的键完全一致,这和需要的层级结构不匹配。 - 第二个示例的约束
{ [K in keyof T]: { [P in keyof T[K]]: Thing } }要求T的每个属性下的所有子属性都是Thing类型,而需求是至少一个子属性为Thing类型,因此无法适配ThingMapC/D这类场景。
解决方案
根据需求,我们需要先定义类型判断逻辑,再约束Handler的泛型参数:
方案1:适配任意SubThing类型属性的Thing
如果你的Thing允许任意属性名对应SubThing类型(符合注释中“其他SubThing类型的属性”的描述),可以这样实现:
interface SubThing { name: string; } // 定义Thing:任意包含至少一个SubThing类型属性的对象 type Thing = Record<string, SubThing>; // 判断类型U是否包含至少一个Thing类型的属性 type HasAtLeastOneThing<U> = Exclude<{ [P in keyof U]: U[P] extends Thing ? P : never }[keyof U], never> extends never ? false : true; // Handler约束:T中至少存在一个属性是UpperThing(包含至少一个Thing类型属性) class Handler<T extends Record<string, unknown> & (HasAtLeastOneThing<T[keyof T]> extends true ? unknown : never)> {} // 测试案例 interface ThingMapA { map: { thing: { subThing: { name : 'subThing name' } } }; } interface ThingMapB { map: { thing: { otherThing: { name : 'subThing name' } } }; } interface ThingMapC { map: { thing: { otherSubThing: { name : 'subThing name' } } }; } interface ThingMapD { map: { otherThing: { otherSubThing: { name : 'subThing name' } } }; } const handlerA = new Handler<ThingMapA>(); // 正常 const handlerB = new Handler<ThingMapB>(); // 正常 const handlerC = new Handler<ThingMapC>(); // 正常 const handlerD = new Handler<ThingMapD>(); // 正常
方案2:严格要求Thing包含subThing属性
如果你的Thing必须严格包含subThing属性,只需调整Thing的定义:
interface SubThing { name: string; } interface Thing { subThing: SubThing; } // 判断类型U是否包含至少一个Thing类型的属性 type HasAtLeastOneThing<U> = Exclude<{ [P in keyof U]: U[P] extends Thing ? P : never }[keyof U], never> extends never ? false : true; // Handler约束:T中至少存在一个属性是UpperThing(包含至少一个Thing类型属性) class Handler<T extends Record<string, unknown> & (HasAtLeastOneThing<T[keyof T]> extends true ? unknown : never)> {} // 测试案例 interface ThingMapA { map: { thing: { subThing: { name : 'subThing name' } } }; } interface ThingMapB { map: { thing: { otherThing: { name : 'subThing name' } } }; } const handlerA = new Handler<ThingMapA>(); // 正常 const handlerB = new Handler<ThingMapB>(); // 报错(因为otherThing不是Thing类型)
内容的提问来源于stack exchange,提问作者Kleywalker
相关产品推荐
相关产品推荐

