如何为动态定义的Drawer类修改函数参数对象并通过TypeScript校验?
解决方案
核心类型工具
先定义两个通用类型工具,用来转换函数的参数类型:
// 移除对象类型中的ctx属性 type OmitCtx<T> = Omit<T, 'ctx'>; // 将目标函数的参数对象中移除ctx属性,生成新的函数类型 type TransformFunc<F> = F extends (args: infer Args) => infer R ? Args extends object ? (args: OmitCtx<Args>) => R : F // 兼容非对象参数场景(当前需求可忽略) : never; // 批量转换draw对象的所有方法类型,生成Drawer需要的方法集合 type DrawerMethods<T> = { [K in keyof T]: TransformFunc<T[K]>; };
完整实现代码
结合动态添加方法的逻辑,完整的TypeScript实现如下:
// 定义ctx的具体类型(以Canvas 2D上下文为例) type CanvasCtx = CanvasRenderingContext2D; // 原始draw对象及类型定义 const draw = { pixel({ ctx, x, y }: { ctx: CanvasCtx; x: number; y: number }): void { ctx.fillRect(x, y, 1, 1); }, line({ ctx, startX, startY, endX, endY }: { ctx: CanvasCtx; startX: number; startY: number; endX: number; endY: number }): void { ctx.beginPath(); ctx.moveTo(startX, startY); ctx.lineTo(endX, endY); ctx.stroke(); } // 更多绘制方法... } as const; // 生成Drawer的完整实例类型 type DrawerInstance = Drawer & DrawerMethods<typeof draw>; // 定义Drawer基础类 class Drawer { ctx: CanvasCtx; constructor(ctx: CanvasCtx) { this.ctx = ctx; } } // 动态为Drawer原型添加所有draw方法 Object.keys(draw).forEach(key => { const methodKey = key as keyof typeof draw; Drawer.prototype[methodKey] = function (args = {}) { return draw[methodKey]({ ctx: this.ctx, ...args }); }; }); // 创建实例并断言类型,获得完整类型提示 const canvas = document.getElementById('canvas') as HTMLCanvasElement; const ctx = canvas.getContext('2d') as CanvasCtx; const drawer = new Drawer(ctx) as DrawerInstance; // 调用时无需传入ctx,TypeScript自动校验参数 drawer.pixel({ x: 10, y: 10 }); drawer.line({ startX: 0, startY: 0, endX: 100, endY: 100 });
无断言替代方案
如果不想使用类型断言,可以通过接口合并让TypeScript自动识别Drawer的方法:
// 生成Drawer的方法类型 type DrawerMethodsType = DrawerMethods<typeof draw>; // 扩展Drawer接口,注入动态方法的类型 interface Drawer extends DrawerMethodsType {} // 后续Drawer类定义、动态添加方法逻辑不变 // 创建实例时直接使用,无需断言 const drawer = new Drawer(ctx);
该方案利用TypeScript的条件类型推断和映射类型,自动复用原始draw对象的类型定义,既避免了重复代码,又让动态添加的方法获得完整的类型校验与提示。
内容的提问来源于stack exchange,提问作者Moarram
相关产品推荐
相关产品推荐

