将继承父接口的子接口传入泛型接口时丢失类型特异性
动态Shape属性类型推导问题
我尝试创建一个动态接口,能够根据Shape的类型获取其对应属性。示例代码如下:
interface Attributes { fill:string|number; width:string|number; height:string|number; stroke:string|number; strokeWidth:string|number; } interface Circle extends Shape { cy:string|number; cx:string|number; r:string|number; } interface Square extends Shape { x:string|number; y:string|number; } interface Polygon extends Shape { points:string|number; } interface Shape extends Partial<Attributes> { [key:string]:string|number|undefined; } type ShapeAttributes<T extends Partial<Attributes>, S> = { [k in keyof T]:S } type ShapeState = { currentShapeState:ShapeAttributes<Shape, string|number | undefined>; } const circle:ShapeAttributes<Circle, string|number> = { fill:"0", stroke:"0", strokeWidth:"0", width:"0", height:"0", cx:"0", cy:"0", r:"0", } const square:ShapeAttributes<Square, string|number> = { fill:"0", stroke:"0", strokeWidth:"0", width:"0", height:"0", x:"0", y:"0", } function shapeState():ShapeState { return { currentShapeState:circle }; } const shape = shapeState(); const innerShape = shape.currentShapeState;
我知道将Shape替换为Circle或Square可以实现需求,但这样属于硬编码,缺乏动态性。在TS Playground中hover查看shape.currentShapeState时,其类型为ShapeAttributes<Shape, string | number | undefined>。我希望能根据传入的具体值(比如circle)自动缩小类型,让innerShape的类型变为ShapeAttributes<Circle,string|number>,请问是否有可行的实现方式?
解决方案
要实现动态推导类型,核心是利用TypeScript的泛型和类型推断能力,让函数根据传入的具体Shape实例自动推导对应类型。具体修改如下:
1. 重构泛型类型定义
将ShapeState改为接受泛型参数的类型,让currentShapeState的类型可以动态关联传入的Shape类型:
type ShapeState<T extends Partial<Attributes>> = { currentShapeState: ShapeAttributes<T, string | number>; };
2. 修改函数为泛型函数
让shapeState函数接收具体的Shape实例作为参数,通过泛型参数自动推断传入的Shape类型:
function shapeState<T extends Partial<Attributes>>(shape: ShapeAttributes<T, string | number>): ShapeState<T> { return { currentShapeState: shape }; }
3. 修复接口循环依赖
原代码中Circle等接口继承Shape,但Shape定义在这些接口之后,会导致循环依赖。调整定义顺序:
interface Shape extends Partial<Attributes> { [key: string]: string | number | undefined; } interface Circle extends Shape { cy: string | number; cx: string | number; r: string | number; } interface Square extends Shape { x: string | number; y: string | number; } interface Polygon extends Shape { points: string | number; }
完整修改后代码
interface Attributes { fill:string|number; width:string|number; height:string|number; stroke:string|number; strokeWidth:string|number; } interface Shape extends Partial<Attributes> { [key:string]:string|number|undefined; } interface Circle extends Shape { cy:string|number; cx:string|number; r:string|number; } interface Square extends Shape { x:string|number; y:string|number; } interface Polygon extends Shape { points:string|number; } type ShapeAttributes<T extends Partial<Attributes>, S> = { [k in keyof T]:S } type ShapeState<T extends Partial<Attributes>> = { currentShapeState:ShapeAttributes<T, string|number>; } const circle:ShapeAttributes<Circle, string|number> = { fill:"0", stroke:"0", strokeWidth:"0", width:"0", height:"0", cx:"0", cy:"0", r:"0", } const square:ShapeAttributes<Square, string|number> = { fill:"0", stroke:"0", strokeWidth:"0", width:"0", height:"0", x:"0", y:"0", } function shapeState<T extends Partial<Attributes>>(shape: ShapeAttributes<T, string | number>): ShapeState<T> { return { currentShapeState: shape }; } const shape = shapeState(circle); const innerShape = shape.currentShapeState; // 类型自动推导为ShapeAttributes<Circle, string | number> const squareShape = shapeState(square); const innerSquare = squareShape.currentShapeState; // 类型自动推导为ShapeAttributes<Square, string | number>
关键说明
- 泛型函数
shapeState通过接收具体的Shape实例,让TypeScript自动推断泛型参数T的类型,从而动态生成对应的ShapeState类型。 - 调整接口定义顺序解决了循环依赖问题,确保类型解析正确。
- 现在无论传入
circle还是square,currentShapeState的类型都会自动缩小为对应的具体Shape属性类型,无需硬编码。
内容的提问来源于stack exchange,提问作者tony willis
相关产品推荐
相关产品推荐

