Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a generic method decorator in Typescript

In this code, I am trying to have a user specify a type for generic decorator function fetchJson.

It would work like this:

  1. Decorate a method with something like: @fetchJson<User>
  2. We then replace the function with one that automatically calls .then(res => res.json()), and give back a typed value wrapped in a Promise.

The issue I am running into is that I do not know how to assign the return descriptor.value to a user-assigned T. Is there a better way to do this? I feel like I am missing something entirely.

interface PromiseDescriptorValue<T>{
  (...args: any[]): Promise<T>;
}

const fetchJson = <T>(
  target: Object,
  propertyKey: string,
  descriptor: TypedPropertyDescriptor<PromiseDescriptorValue<Response>> // Response is a whatwg-fetch response -- https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/whatwg-fetch/whatwg-fetch.d.ts#L58
): TypedPropertyDescriptor<PromiseDescriptorValue<T>> => {
  const oldMethod = descriptor.value;

  descriptor.value = function(...args: any[]) {
    return oldMethod.apply(this, args).then((res: Response) => res.json());
  };

  return descriptor;
};


// TS2322: Type 'TypedPropertyDescriptor<PromiseDescriptorValue<Response>>'
// is not assignable to type
// 'TypedPropertyDescriptor<PromiseDescriptorValue<T>>'. Type
// 'PromiseDescriptorValue<Response>' is not assignable to type
// 'PromiseDescriptorValue<T>'. Type 'Response' is not assignable to type 'T'.
like image 259
ianks Avatar asked Jan 06 '16 16:01

ianks


People also ask

How do I create a custom decorator in TypeScript?

In TypeScript, you can create decorators using the special syntax @expression , where expression is a function that will be called automatically during runtime with details about the target of the decorator. The target of a decorator depends on where you add them.

What is decorator in TypeScript?

A Decorator is a special kind of declaration that can be attached to a class declaration, method, accessor, property, or parameter. Decorators use the form @expression , where expression must evaluate to a function that will be called at runtime with information about the decorated declaration.

Are decorators still experimental in TypeScript?

NOTE: Decorators are an experimental feature and are still in stage 2 in JavaScript, also, experimental in TypeScript, so it might change in the future.

How do you define a generic type in TypeScript?

Generics allow creating 'type variables' which can be used to create classes, functions & type aliases that don't need to explicitly define the types that they use. Generics makes it easier to write reusable code.


1 Answers

Maybe something like this:

interface PromiseDescriptorValue<T>{
  (...args: any[]): Promise<T>;
}

export function fetchJson<T>(): any
{
    return (
        target: Object,
        propertyKey: string,
        descriptor: any): TypedPropertyDescriptor<PromiseDescriptorValue<T>> => 
        {
        const oldMethod = descriptor.value;

        descriptor.value = function(...args: any[]) 
        {
            return oldMethod.apply(this, args).then((res: Response) => res.json());
        };

        return descriptor;
        };
}

class C 
{
    @fetchJson<User>()
    foo(args) 
    {
        //....
    }
}
like image 147
Amid Avatar answered Oct 14 '22 14:10

Amid