How to inpect Async/Promise using Reflect.getMetadata

147 Views Asked by At

I found this bug when i'm testing https://github.com/ztytotoro/rxjs-extension/blob/master/src/decorators.ts#L20

export const AsyncFunction = async function () {}.constructor;

export function isAsyncOrPromise(fn: any): boolean {
  return fn instanceof Promise || fn instanceof AsyncFunction;
}

⏯ Playground Link

Playground Link: Provided

Code

function Log(target: any, key: string | symbol, descriptor: PropertyDescriptor): void {
  const type = Reflect.getMetadata('design:returntype', target, key);
  console.log(key, type.constructor.name);
}

class Demo {
  @Log
  async save(n: number): Promise<string> {
    const result = await Promise.resolve(`${n} succeed`);
    return result;
  }
}
const demo = new Demo();

async function main(): Promise<any> {
  console.log('main', main.constructor.name);
  const result = await demo.save(1);
}

main();

Actual behavior

save Function
main AsyncFunction

Expected behavior

save AsyncFunction
main AsyncFunction

Demo2

function Log(target: any, key: string | symbol, descriptor: PropertyDescriptor): void {
  const type = Reflect.getMetadata('design:returntype', target, key);
  console.log(key, type instanceof Promise);
}

class Demo {
  @Log
  async save(n: number): Promise<string> {
    const result = await Promise.resolve(`${n} succeed`);
    return result;
  }
}
const demo = new Demo();

async function main(): Promise<any> {
  const result = await demo.save(1);
}

main();

Playground Link: Provided

Actual behavior

save false

Expected behavior

save true
1

There are 1 best solutions below

0
On

was hard to find how to solve it - and I can't explain why it works

For Demo 1 I think this is the correct implementation:

function Log(target: any, key: string | symbol, descriptor: PropertyDescriptor): void {
  const type = Reflect.getMetadata('design:returntype', target, key);
  console.log(key, type.name); // 'Promise'
}

class Demo {
  @Log
  async save(n: number): Promise<string> {
    const result = await Promise.resolve(`${n} succeed`);
    return result;
  }
}
const demo = new Demo();

async function main(): Promise<any> {
  console.log('main', main.constructor.name); // 'AsyncFunction'
  const result = await demo.save(1);
}

main();

Demo 2 code

function Log(target: any, key: string | symbol, descriptor: PropertyDescriptor): void {
  const type = Reflect.getMetadata('design:returntype', target, key);
  console.log(key, type === Promise); // true
}

class Demo {
  @Log
  async save(n: number): Promise<string> {
    const result = await Promise.resolve(`${n} succeed`);
    return result;
  }
}
const demo = new Demo();

async function main(): Promise<any> {
  const result = await demo.save(1);
}

main();