0

I'm working on decorators for my API methods and I encoutered this problem. I'm trying to overload decorators and get the 'This overload signature is not compatible with its implementation signature' error lintered.

Here goes fragment of code I created:

export function ApiMethod<TResponse extends ApiResponse>(options: { methodName: string }): 
    (target: any, propertyName: string, descriptor: TypedPropertyDescriptor<() => Promise<TResponse>>) => void    

export function ApiMethod<TResponse extends ApiResponse>(options: { methodName: string, accessPolicy: MethodAccessPolicy }): 
    (target: any, propertyName: string, descriptor: TypedPropertyDescriptor<(payload: JwtPayload) => Promise<TResponse>>) => void   

export function ApiMethod<TParams extends Object, TResponse extends ApiResponse>(options: { methodName: string, paramsType: (new (...args: Array<any>) => TParams) }): 
    (target: any, propertyName: string, descriptor: TypedPropertyDescriptor<(params: TParams) => Promise<TResponse>>) => void  
 
// Here is the error   
export function ApiMethod<TParams extends Object, TResponse extends ApiResponse>(options: { methodName: string, paramsType: (new (...args: Array<any>) => TParams), accessPolicy: MethodAccessPolicy }): 
    (target: any, propertyName: string, descriptor: TypedPropertyDescriptor<(params: TParams, payload: JwtPayload) => Promise<TResponse>>) => void   

export function ApiMethod<TParams extends Object, TResponse extends ApiResponse>(options: MethodOptions | ParameterizedMethodOptions<TParams>): 
    (target: any, propertyName: string, descriptor: TypedPropertyDescriptor<(params?: TParams, payload?: JwtPayload) => Promise<TResponse>>) => void    
{
    return (target: any, propertyName: string, descriptor: TypedPropertyDescriptor<(params?: TParams, payload?: JwtPayload) => Promise<TResponse>>) => {
        
    };
}

interface MethodOptions {
    methodName: string;
    accessPolicy?: MethodAccessPolicy;
}

interface ParameterizedMethodOptions<TParams> extends MethodOptions {
    paramsType?: (new (...args: Array<any>) => TParams);
}

I set this general restriction like that so the decorated method can have either params or payload or both, depending on which overload you do use:

(target: any, propertyName: string, descriptor: TypedPropertyDescriptor<(params?: TParams, payload?: JwtPayload) => Promise<TResponse>>) => void

All the overloads are working - I can set no parameters whatsoever, only payload, only parameters, but I can't set payload and parameters because of the error.

Any help would be appreciated. Thanks in advance.

Lauriero
  • 14
  • 4

1 Answers1

0

You did not provide the type of JwtPayload and I assumed it was Record<string, any> which allowed me to reproduce the error.

This is because the return type of the failing overload is different from the implementation signature.

(target: any, propertyName: string, descriptor: TypedPropertyDescriptor<(params: TParams, payload: JwtPayload) => Promise<TResponse>>) => void  

Versus

(target: any, propertyName: string, descriptor: TypedPropertyDescriptor<(params?: TParams, payload?: JwtPayload) => Promise<TResponse>>) => void

You'll have to union the return type:

export function ApiMethod<TParams extends Object, TResponse extends ApiResponse>(options: MethodOptions | ParameterizedMethodOptions<TParams>): 
    ((target: any, propertyName: string, descriptor: TypedPropertyDescriptor<(params: TParams, payload: JwtPayload) => Promise<TResponse>>) => void) | 
    ((target: any, propertyName: string, descriptor: TypedPropertyDescriptor<(params?: TParams, payload?: JwtPayload) => Promise<TResponse>>) => void) 
{
    return (target: any, propertyName: string, descriptor: TypedPropertyDescriptor<(params?: TParams, payload?: JwtPayload) => Promise<TResponse>>) => {
        
    };
}

Playground Link

As for why this happens, I believe it is because TParams is a generic while JwtPayload is not, and as such TS cannot guarantee JwtPayload is instantiated with the same type or intended type across overloads.

Cody Duong
  • 2,292
  • 4
  • 18
  • Thanks a lot. I thought this might be a possible solution, but I don't understand why this works. In my question in the second overload I have payload: JwtPayload and it just works. Maybe it's because the descriptor also gets validated for overload in his parameters and JwtPayload takes place of TParam because JwtPayload can be TParam. If that so, then this is clear. – Lauriero Mar 22 '22 at 15:53
  • JwtPayload is a plain object with some properties btw. – Lauriero Mar 22 '22 at 15:53
  • @Lauriero I did some testing, and it looks like you're right. The descriptor has validation, and in the case of overload 2, since JwtPayload is a type of object, it can replace the TParam, but this is not true in the case of overload 4. In overload 4, JwtPayload is assigned to payload (as it should), but it can be instantiated with different subtypes of JwtPayload, so this overload errors out. This a whole topic upon itself: https://stackoverflow.com/a/59363875/17954209 – Cody Duong Mar 22 '22 at 18:57