如何为带动态参数方法的动态对象添加TypeScript类型
如何为invokeActions方法添加TypeScript类型以校验参数错误
我实现了如下方法:
const invokeActions = (target, actions) => { actions.forEach(({ action, args }) => { target[action](...args); }); }
需要为该方法添加正确的TypeScript类型,让编译器能检测出调用函数时的参数数量错误、参数类型错误。使用示例如下:
const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); const actions = [ { action: 'moveTo', args: [0, 0] }, { action: 'lineTo', args: [100, 100] }, { action: 'closePath', args: ['error'] }, // 此处应该触发TypeScript编译错误 { action: 'closePath', args: [1, 2] }, // 同样应该触发错误 ]; invokeActions(ctx, actions);
我曾尝试用interface定义类型,但closePath被错误地允许传入[]和[number, number],不符合预期:
interface Drawable { moveTo: (x: number, y: number) => void; lineTo: (x: number, y: number) => void; closePath: () => void; } type ActionType = { action: Drawable[keyof Drawable] extends (...args: any[]) => void ? keyof Drawable : never; args: Drawable[keyof Drawable] extends (...args: any[]) => void ? Drawable[keyof Drawable] : never; }
我还尝试过结合infer、Parameters<T[keyof T]>等方式,但都没能解决问题。
解决方案
要实现严格的参数校验,需要让每个Action的args类型与对应action方法的参数类型严格匹配,这里可以用映射类型结合联合类型来实现:
// 定义通用的Action类型,针对目标对象T的每个方法生成对应的动作类型 type Action<T> = { [K in keyof T]: T[K] extends (...args: infer Args) => void ? { action: K; args: Args } : never }[keyof T]; // 为invokeActions添加泛型类型 const invokeActions = <T extends Record<string, (...args: any[]) => void>>( target: T, actions: Action<T>[] ) => { actions.forEach(({ action, args }) => { target[action](...args); }); };
类型说明:
Action<T>类型:- 遍历目标对象
T的所有键K,判断T[K]是否是函数 - 如果是函数,用
infer Args提取出该函数的参数类型,生成{ action: K; args: Args }的类型 - 最后通过
[keyof T]将映射类型转为联合类型,这样每个动作都会严格对应自己的参数类型
- 遍历目标对象
invokeActions的泛型定义:- 用
T extends Record<string, (...args: any[]) => void>约束目标对象必须是包含函数属性的对象 actions参数指定为Action<T>[],这样数组中的每个元素都必须是符合对应方法参数要求的动作对象
- 用
效果验证:
在示例中:
{ action: 'closePath', args: ['error'] }会触发错误,因为closePath的参数类型是[],传入['error']不匹配{ action: 'closePath', args: [1, 2] }同样会触发错误,参数数量不匹配- 而
moveTo和lineTo传入正确的[number, number]参数则不会有问题
该类型定义也能自动适配可选参数、剩余参数的场景,因为infer Args会完整提取函数的参数类型信息。
内容的提问来源于stack exchange,提问作者doggyxomax
相关产品推荐
相关产品推荐

