Typescript: Newline after `async` keyword generates incorrect code

Created on 29 Mar 2019  路  7Comments  路  Source: microsoft/TypeScript


TypeScript Version: 3.4.0-dev.20190329


Search Terms:

async, newline

Code

// A *self-contained* demonstration of the problem follows...
// Test this by running `tsc` on the command-line, rather than through another build tool such as Gulp, Webpack, etc.
class
Foo {
    constructor(private foo) {
        console.log(`Creating Foo ${this.foo}...`);
    }

    static // a newline after 'static' is okay.
        create() {
        return new Foo('static');
    }

    async // a newline after 'async' makes `createAsync()` a normal function.
    createAsync() {
        return new Foo('async');
    }
}

Foo.create().createAsync().then(x => console.log(x)); // createAsync() does not return a promise.

Expected behavior:
If there is a newline between the async keyword and the function identifier, I expect the function to be an asynchronous function. A newline, for example, is allowed between the static keyword and the function identifier.

Actual behavior:
The function is not an asynchronous function and tsc returns the following error:

error TS2339: Property 'then' does not exist on type 'Foo'.

Playground Link:
https://www.typescriptlang.org/play/index.html#src=class%0D%0AFoo%20%7B%0D%0A%20%20%20%20constructor(private%20foo)%20%7B%0D%0A%20%20%20%20%20%20%20%20console.log(%60Creating%20Foo%20%24%7Bthis.foo%7D...%60)%3B%0D%0A%20%20%20%20%7D%0D%0A%0D%0A%20%20%20%20static%20%2F%2F%20a%20newline%20after%20'static'%20is%20okay.%0D%0A%20%20%20%20%20%20%20%20create()%20%7B%0D%0A%20%20%20%20%20%20%20%20return%20new%20Foo('static')%3B%0D%0A%20%20%20%20%7D%0D%0A%0D%0A%20%20%20%20async%20%2F%2F%20a%20newline%20after%20'async'%20makes%20%60createAsync()%60%20a%20normal%20function.%0D%0A%20%20%20%20createAsync()%20%7B%0D%0A%20%20%20%20%20%20%20%20return%20new%20Foo('async')%3B%0D%0A%20%20%20%20%7D%0D%0A%7D%0D%0A%0D%0AFoo.create().createAsync().then(x%20%3D%3E%20console.log(x))%3B

Related Issues:

Working as Intended

Most helpful comment

async is NOT a keyword, itself is a valid identifier. By separating them you're declaring a class field named async.

All 7 comments

I think typescript is correct. Running the js version of your code in node:

class Foo {
    constructor(foo) {
        this.foo = foo;
        console.log(`Creating Foo ${this.foo}...`);
    }
    static // a newline after 'static' is okay.
     create() {
        return new Foo('static');
    }
    async // a newline after 'async' makes `createAsync()` a normal function.
    createAsync() {
        return new Foo('async');
    }
}
Foo.create().createAsync().then(x => console.log(x));

results in:

Z:\tmp\ts\index.js:11
    createAsync() {
    ^^^^^^^^^^^

SyntaxError: Unexpected identifier
    at createScript (vm.js:80:10)
    at Object.runInThisContext (vm.js:139:10)
    at Module._compile (module.js:617:28)
    at Object.Module._extensions..js (module.js:664:10)
    at Module.load (module.js:566:32)
    at tryModuleLoad (module.js:506:12)
    at Function.Module._load (module.js:498:3)
    at Function.Module.runMain (module.js:694:10)
    at startup (bootstrap_node.js:204:16)
    at bootstrap_node.js:625:3

@DanielRosenwasser maybe typescript should report a better error in this case?

async is NOT a keyword, itself is a valid identifier. By separating them you're declaring a class field named async.

@trotyl That's also why TypeScript reports an error if you have noImplicitAny enabled (which I think you should).

https://typescript-play.js.org/#code/MYGwhgzhBQBiD28AEBvaSNOPAdhALgE4Cuw+8hAFAA6ECWAbmPgKZIBmiAlKupv9jzwQLAHQh4Ac0oADAMKEWzOjklIEyACQp8ACzoRRneAF9R5mVwDcfDCei2kBZcCQB6N0jBIcLAO4gKmxg7KyESADkzvh0wBFIBkjwANZgAJ6ijgKKzCyUPGj8-Ir4xIQ4Pv7qiJRR+C4R1o72jpBpOK4eXpUBQV6hLOERbR3xALZgySwQSDLAOawAghDtwPky3TgUEyAcxB0xuJnZSksrHfm8RZglZRW+ftXwtSNxTfz29kA

Not to pile on, but the grammar for an AsyncMethod in the ECMAScript spec is basically

AsyncMethod [no LineTerminator here] PropertyName(UniqueFormalParameters) {
    AsyncFunctionBody
}

maybe typescript should report a better error in this case?

I don't know what error we can give except for in noImplicitAny, which isn't a bad idea. Feel free to open a PR!

I don't see why TypeScript should raise any error, as this is legal code. Besides noImplicitAny (which is unrelated) this sounds like a case for a linter rule.

This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

manekinekko picture manekinekko  路  3Comments

Roam-Cooper picture Roam-Cooper  路  3Comments

bgrieder picture bgrieder  路  3Comments

blendsdk picture blendsdk  路  3Comments

remojansen picture remojansen  路  3Comments