Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parameter decorators use incorrect async/await context, generated code has syntax error #48509

Closed
evanw opened this issue Apr 1, 2022 · 2 comments Β· Fixed by #50040
Closed

Parameter decorators use incorrect async/await context, generated code has syntax error #48509

evanw opened this issue Apr 1, 2022 · 2 comments Β· Fixed by #50040
Assignees
Labels
Bug A bug in TypeScript Fix Available A PR has been opened for this issue

Comments

@evanw
Copy link
Contributor

evanw commented Apr 1, 2022

Bug Report

πŸ”Ž Search Terms

parameter decorator async await context syntax error

πŸ•— Version & Regression Information

  • This is the behavior in every version I tried

⏯ Playground Link

Playground link with relevant code

πŸ’» Code

function decorator(a: any): any {}
function fn(value: Promise<number>): any {
  class Class {
    async method(@decorator(await value) arg: number) {}
  }
  return Class
}

πŸ™ Actual behavior

The above sample code is compiled without any errors by TypeScript and generates the following invalid JavaScript code, which contains a syntax error:

var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
function decorator(a) { }
function fn(value) {
    class Class {
        async method(arg) { }
    }
    __decorate([
        __param(0, decorator(await value))
    ], Class.prototype, "method", null);
    return Class;
}

The specific syntax error is that await is used outside of an async function. I originally discovered this issue with the following incorrect code:

function decorator(a: any): any {}
function fn(value: Promise<number>): any {
  class Class {
    method(@decorator(await value) arg: number) {}
  }
  return Class
}

That is correctly detected as an error by TypeScript, but it has the wrong suggestion:

example.ts:4:23 - error TS1308: 'await' expressions are only allowed within async functions and at the top levels of modules.

4     method(@decorator(await value) arg: number) {}
                        ~~~~~

  example.ts:4:5
    4     method(@decorator(await value) arg: number) {}
          ~~~~~~
    Did you mean to mark this function as 'async'?


Found 1 error in example.ts:4

The async should be added to the outer function, not to the inner method.

πŸ™‚ Expected behavior

Both the error message suggestion and the compiler's validation logic itself are incorrect about the async/await context of parameter decorators. It should be the context of the class, not the context of the method, because the generated decorator code is inserted as sibling statements of the class. In other words adding async on the outer function like this should be the only valid way to fix this code:

function decorator(a: any): any {}
async function fn(value: Promise<number>): any {
  class Class {
    method(@decorator(await value) arg: number) {}
  }
  return Class
}

The additional context here is that I'm the developer behind esbuild and I'm trying to reverse-engineer how I should be converting TypeScript code to JavaScript code. A user sent me an issue with a similar problem in esbuild: evanw/esbuild#2147. When investigating that bug I discovered this bug in TypeScript and reported it here.

@evanw
Copy link
Contributor Author

evanw commented Apr 1, 2022

There's also a similar case with generator functions and yield (link):

function decorator(a: any): any {}
function *fn(value: Promise<number>): any {
  class Class {
    method(@decorator(yield value) arg: number) {}
  }
  return Class
}

Right now TypeScript reports an error but still generates valid JavaScript. It would also be good to have clarity on whether this case is supposed to work or not as well.

@typescript-bot typescript-bot added the Fix Available A PR has been opened for this issue label Jul 25, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript Fix Available A PR has been opened for this issue
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants