Typescript: MethodDecorator gets TS2322/TS2315

Created on 21 Aug 2017  路  8Comments  路  Source: microsoft/TypeScript



TypeScript Version: 2.4.2
Code

export function route(method: string, path: string): MethodDecorator {
    return (target: any, name: string, descriptor: TypedPropertyDescriptor<Function>) => {
        routeManager.regeisterRoute({
            constructor: target.constructor,
            function: descriptor.value,
            method,
            path
        });
    }
}
export function route(method: string, path: string): MethodDecorator<Function> {
    return (target: any, name: string, descriptor: TypedPropertyDescriptor<Function>) => {
        routeManager.regeisterRoute({
            constructor: target.constructor,
            function: descriptor.value,
            method,
            path
        });
    }
}



md5-f96297b63fb35a729f69dab9d2abc379



error TS2322: Type '(target: any, name: string, descriptor: TypedPropertyDescriptor<Function>) => void' is not assignable to type 'MethodDecorate
or'.
  Types of parameters 'descriptor' and 'descriptor' are incompatible.
    Type 'TypedPropertyDescriptor<T>' is not assignable to type 'TypedPropertyDescriptor<Function>'.
      Type 'T' is not assignable to type 'Function'.



md5-263ea59b36b5a390ecb20970d96a9929



error TS2315: Type 'MethodDecorator' is not generic.
Bug Decorators

Most helpful comment

For the first one, it is caused by stricter generic checks (#16368).

It seems you have to define a CustomMethodDecorator first, since there is no generic on MethodDecorator, and its declaration requires a non-constraint generic.

Or just enable the --noStrictGenericChecks option.


For the second one, there is indeed no generic on MethodDecorator, the error message seems a little weird.

(lib.d.ts)

declare type MethodDecorator = <T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>) => TypedPropertyDescriptor<T> | void;

All 8 comments

For the first one, it is caused by stricter generic checks (#16368).

It seems you have to define a CustomMethodDecorator first, since there is no generic on MethodDecorator, and its declaration requires a non-constraint generic.

Or just enable the --noStrictGenericChecks option.


For the second one, there is indeed no generic on MethodDecorator, the error message seems a little weird.

(lib.d.ts)

declare type MethodDecorator = <T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>) => TypedPropertyDescriptor<T> | void;

CustomMethodDecorator or --noStrictGenericChecks will works, but I think it should infer T as Function in case 1, or make MethodDecorator generic in case 2.

MethodDecorator is defined as <T>(...something: SomeType[]): SomeOutput<T>;

So that it'll accept all kinds of T without any constraint, but in your case 1 it only accept Function, it is indeed not compatible from stricter perspective.

NOTE: With --noStrictGenericChecks enabled, all generics will be considered any before comparing.


make MethodDecorator generic in case 2.

I'm not familiar with that MethodDecorator type, it'd be better to raise another issue for that things.

There is no requirements that decorators return one of the predefined decorator types in lib.d.ts.

I would strongly advise against enabling --noStrictGenericChecks and instead using the following (which actually has a richer type but passes the checker thanks to inference.

declare const routeManager: {
  regeisterRoute: (config: {
    constructor?: Function,
    function?: Function,
    method: string,
    path: string
  }) => void;
};

export function route(method: string, path: string) {
  return <T extends object,
        K extends keyof T,
        R extends Promise<any>>
      (target: T, name: keyof T, descriptor: TypedPropertyDescriptor<(...args: any[]) => R>) => {
        routeManager.regeisterRoute({
        constructor: target.constructor,
        function: descriptor.value,
        method,
        path
      });
  };
}

class A {
  @route('GET', 'api/values') m() {
    return Promise.resolve(['a', 'b']);
  }
}

Not only will this pass the type checker since it is a valid decorator type, but the decorator will validate that the decorated method actually returns a Promise for a resource (just an example).

Trying to exactly match the predefined decorator types would preclude this very useful typechecking behavior.

Well, it works. Just like what @ikatyang says to define a CustomMethodDecorator.

@Norgerman Ah, I missed that comment. Sorry for being redundant.

@Norgerman Could you please share your way of implementing CustomMethodDecorator?

@petispaespea see this comment

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Roam-Cooper picture Roam-Cooper  路  3Comments

seanzer picture seanzer  路  3Comments

dlaberge picture dlaberge  路  3Comments

siddjain picture siddjain  路  3Comments

manekinekko picture manekinekko  路  3Comments