TypeScript方法装饰器如何限制函数含任意位置可选参数c?:T
TypeScript装饰器约束:仅允许修饰带指定可选参数的函数
要让@method装饰器仅允许修饰**包含被@param标记的可选参数c?: T**的函数,你之前尝试的[...any[], T?, ...any[]]写法无效,因为TypeScript不支持中间包含可选元素的元组语法。下面是可行的解决方案:
1. 定义编译时类型约束
用递归条件类型检查函数参数列表中是否存在T | undefined类型的参数(对应可选的c?: T):
// 递归检查参数元组中是否存在T | undefined类型的元素 type IncludesOptional<T, Args extends any[]> = Args extends [infer First, ...infer Rest] ? First extends T | undefined ? true : IncludesOptional<T, Rest> : false; // 约束函数必须包含至少一个可选的T类型参数 type HasOptionalC<T> = (...args: any[]) => any extends infer F ? F extends (...args: infer Args) => any ? IncludesOptional<T, Args> extends true ? F : never : never : never;
2. 修改@method装饰器
更新装饰器的泛型约束,同时加入运行时检查,确保函数确实被@param标记:
const contexts = new WeakMap<object, number>(); // 给method添加泛型T,明确约束的参数类型 function method<T>() { return <F extends HasOptionalC<T>>( target: any, key: string | symbol, descriptor: TypedPropertyDescriptor<F> ): TypedPropertyDescriptor<F> => { // 运行时验证:确保方法存在被@param标记的参数 if (!contexts.has(target[key])) { throw new Error(`方法 "${String(key)}" 必须包含一个被@param装饰的可选参数`); } return descriptor; }; } function param( target: Object, key: string | symbol, index: number ) { const method = target[key]; contexts.set(method, index); }
3. 使用示例
先定义T的具体类型,再在类中使用装饰器:
// 示例:定义T为字符串类型 type T = string; class C { // ❌ 编译报错,运行时也会抛出错误:无符合要求的参数 @method<T>() f(a: number, b: string, d?: boolean, e?: number) { } // ✅ 编译通过,运行时正常:包含被@param标记的可选参数c?: T @method<T>() g(a: number, b: string, @param c?: T, d?: boolean, e?: number) { } }
关键说明
- 编译时约束:通过
HasOptionalC<T>类型强制函数参数列表中存在可选的T类型参数,不符合的函数会在编译阶段直接报错。 - 运行时检查:利用
WeakMap记录被@param标记的方法,在@method中做二次验证,避免绕过类型检查的场景。
内容的提问来源于stack exchange,提问作者CMCDragonkai
相关产品推荐
相关产品推荐

