Skip to content

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

@evanw

Description

@evanw

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.

Metadata

Metadata

Assignees

Labels

BugA bug in TypeScriptFix AvailableA PR has been opened for this issue

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions