如何通过类型强制两函数签名一致?含参数顺序相反场景
解决方案
一、基础场景:fnGenerator 参数顺序反向的签名兼容校验
你遇到的核心问题是泛型约束方向搞反了——原代码要求Original extends Copy(即原函数兼容复制函数),但实际需要的是Copy extends Original(复制函数兼容原函数)。以下是两种修正方式:
方式1:直接泛型约束
通过泛型明确要求复制函数必须是原函数的子类型(可赋值给原函数):
function fnGenerator<Original extends Function, Copy extends Original>(copy: Copy, original: Original) {} function original(props:{a?:number,z:number}):number { return 2} function copy(props:{a:number}):number { return 1} fnGenerator(copy,original) // 编译错误指向copy,符合预期
方式2:精确参数/返回值约束(推荐)
使用TypeScript内置工具类型Parameters和ReturnType,更精确地约束复制函数的参数和返回值与原函数兼容:
function fnGenerator<Original extends (...args: any[]) => any>( copy: (...args: Parameters<Original>) => ReturnType<Original>, original: Original ) {} function original(props:{a?:number,z:number}):number { return 2} function copy(props:{a:number}):number { return 1} fnGenerator(copy,original) // 编译错误指向copy,符合预期
二、实际场景:fallback装饰器的签名校验
当前装饰器代码的问题是泛型仅约束被装饰方法适配fallback函数的类型,而非反过来。我们需要在装饰器中捕获被装饰方法的类型,并强制fallback函数兼容该类型:
修正后的装饰器代码
function fallback<FallbackFn>(props: { fallback: FallbackFn }) { return function <OriginalFn>( target: any, propertyKey: string, // 通过条件类型触发错误:若fallback不兼容原方法,则要求descriptor包含不存在的属性 descriptor: Omit<PropertyDescriptor, 'value'> & { value: OriginalFn } & (FallbackFn extends OriginalFn ? {} : { __INCOMPATIBLE__: never }) ) { return descriptor; }; }
更精确的兼容性校验(可选)
如果需要明确区分参数不兼容还是返回值不兼容,可以扩展条件类型:
function fallback<FallbackFn extends (...args: any[]) => any>(props: { fallback: FallbackFn }) { return function <OriginalFn extends (...args: any[]) => any>( target: any, propertyKey: string, descriptor: Omit<PropertyDescriptor, 'value'> & { value: OriginalFn } & ( // 函数兼容性规则:参数逆变,返回值协变 Parameters<OriginalFn> extends Parameters<FallbackFn> ? ReturnType<FallbackFn> extends ReturnType<OriginalFn> ? {} : { __ERROR__: "Fallback返回值与原方法不兼容" } : { __ERROR__: "Fallback参数与原方法不兼容" } ) ) { return descriptor; }; }
测试示例
class MyClass1 { // 触发编译错误:fallback要求z必须存在,而method1允许z可选,签名不兼容 @fallback({fallback: (props: {x:number,z:number})=>{ return 0} }) static method1(props:{x:number, z?: number}):number { return 1 } }
内容的提问来源于stack exchange,提问作者Stav Alfi
相关产品推荐
相关产品推荐

