如何在TypeScript中实现泛型策略模式并解决类型编译问题
解决TypeScript策略模式中的类型匹配问题
原模型代码
enum MyType { CHILD_X = "CHILD_X", CHILD_Y = "CHILD_Y" } abstract class ParentObject { readonly type: MyType; p: number; constructor(type: MyType, p: number) { this.type = type; this.p = p; } public abstract returnMyself(): ParentObject; } class ChildXObject extends ParentObject { x: string; constructor(p: number, x: string) { super(MyType.CHILD_X, p); this.x = x; } public returnMyself() { return this; } } class ChildYObject extends ParentObject { y: string; constructor(p: number, y: string) { super(MyType.CHILD_Y, p); this.y = y; } public returnMyself(): ChildYObject { return this; } }
遇到的类型错误
尝试1:类型交集错误
type PrintTypeToClass = { CHILD_X: ChildXObject CHILD_Y: ChildYObject } const RENDERERS: {[key in MyType]: (p: PrintTypeToClass[key]) => void} = { [MyType.CHILD_X]: function(x: ChildXObject): void { console.log(x.p); console.log(x.x); }, [MyType.CHILD_Y]: function(y: ChildYObject): void { console.log(y.p); console.log(y.y); } }; type Objs = ChildXObject | ChildYObject const listOfObjects: Objs[] = [ new ChildXObject(0, ""), new ChildYObject(0, "") ]; listOfObjects.map(obj => { const fn = RENDERERS[obj.type]; fn(obj); // Argument of type 'Objs' is not assignable to parameter of type 'ChildXObject & ChildYObject'. })
报错信息:Argument of type 'Objs' is not assignable to parameter of type 'ChildXObject & ChildYObject'
尝试2:泛型函数不兼容错误
const RENDERERS: {[key in MyType]: <T extends ParentObject>(p: T) => void} = { // Type of computed property's value is '<T extends ParentObject>(x: ChildXObject) => void', which is not assignable to type '<T extends ParentObject>(p: T) => void'. [MyType.CHILD_X]: function(x: ChildXObject): void { console.log(x.p); console.log(x.x); }, [MyType.CHILD_Y]: function(y: ChildYObject): void { console.log(y.p); console.log(y.y); } }; const listOfObjects: ParentObject[] = [ new ChildXObject(0, ""), new ChildYObject(0, "") ]; listOfObjects.map(obj => { const fn = RENDERERS[obj.type]; fn(obj); })
报错信息:Type of computed property's value is '<T extends ParentObject>(x: ChildXObject) => void', which is not assignable to type '<T extends ParentObject>(p: T) => void'
解决方案
方案1:利用泛型函数关联类型
通过泛型函数明确对象类型与type字段的对应关系,让TypeScript正确推断匹配的渲染函数:
type PrintTypeToClass = { [MyType.CHILD_X]: ChildXObject; [MyType.CHILD_Y]: ChildYObject; }; const RENDERERS: { [K in MyType]: (obj: PrintTypeToClass[K]) => void } = { [MyType.CHILD_X]: (obj) => { console.log(obj.p, obj.x); }, [MyType.CHILD_Y]: (obj) => { console.log(obj.p, obj.y); } }; type Objs = PrintTypeToClass[MyType]; const listOfObjects: Objs[] = [ new ChildXObject(0, "x"), new ChildYObject(0, "y") ]; // 泛型函数确保类型严格匹配 function render<K extends MyType>(obj: PrintTypeToClass[K]) { RENDERERS[obj.type](obj); } listOfObjects.forEach(render);
方案2:使用类型守卫显式推断
自定义类型守卫函数,在运行时判断对象具体类型,从而匹配对应渲染逻辑:
// 类型守卫函数:判断对象是否为ChildXObject function isChildX(obj: ParentObject): obj is ChildXObject { return obj.type === MyType.CHILD_X; } // 类型守卫函数:判断对象是否为ChildYObject function isChildY(obj: ParentObject): obj is ChildYObject { return obj.type === MyType.CHILD_Y; } // 定义渲染器(同尝试1的RENDERERS) type PrintTypeToClass = { CHILD_X: ChildXObject CHILD_Y: ChildYObject } const RENDERERS: {[key in MyType]: (p: PrintTypeToClass[key]) => void} = { [MyType.CHILD_X]: function(x: ChildXObject): void { console.log(x.p); console.log(x.x); }, [MyType.CHILD_Y]: function(y: ChildYObject): void { console.log(y.p); console.log(y.y); } }; const listOfObjects: ParentObject[] = [ new ChildXObject(0, ""), new ChildYObject(0, "") ]; listOfObjects.forEach(obj => { if (isChildX(obj)) { RENDERERS[MyType.CHILD_X](obj); } else if (isChildY(obj)) { RENDERERS[MyType.CHILD_Y](obj); } });
方案3:类型断言快速修复(简洁场景)
若能确保类型匹配的准确性,可使用类型断言快速绕过编译错误:
type PrintTypeToClass = { CHILD_X: ChildXObject CHILD_Y: ChildYObject } const RENDERERS: {[key in MyType]: (p: PrintTypeToClass[key]) => void} = { [MyType.CHILD_X]: function(x: ChildXObject): void { console.log(x.p); console.log(x.x); }, [MyType.CHILD_Y]: function(y: ChildYObject): void { console.log(y.p); console.log(y.y); } }; type Objs = ChildXObject | ChildYObject const listOfObjects: Objs[] = [ new ChildXObject(0, ""), new ChildYObject(0, "") ]; listOfObjects.map(obj => { const fn = RENDERERS[obj.type]; // 断言obj为当前type对应的具体类型 fn(obj as PrintTypeToClass[typeof obj.type]); });
内容的提问来源于stack exchange,提问作者spxcxlxxs
相关产品推荐
相关产品推荐

