Typescript: Compiler does not allow abstract generators

Created on 17 Jul 2018  ·  14Comments  ·  Source: microsoft/TypeScript


TypeScript Version: First noticed on 2.9.1, confirmed in 3.0.0-dev.20180712


Search Terms:
generator abstract, "An overload signature cannot be declared as a generator.", generator overload, overload generator abstract, TS1222

Code

abstract class SomethingAbstract {
    // TS1222: An overload signature cannot be declared as a generator.
    abstract *sadGenerator(): any; // specifying a return type doesn't help
    *fineGenerator(): any {}
}

Expected behavior:
The compiler should allow abstract generator methods.

Actual behavior:
The compiler does not allow abstract generator methods: instead, it errors with: [ts] An overload signature cannot be declared as a generator.

I believe I know where and why this is and I'll open up a PR once I fix it.

Playground Link: Small sample

Related Issues:
Couldn't find any related ones

Working as Intended

Most helpful comment

I do not think the original issue is a bug. generators, just like async functions, are just an implementation detail. the contract does not care how the function is implemented but rather what parameters it takes, and what return type it offers. A generator function implements the Iterator protocol, but it can also be implemented using different means, and there is no reason derived classes has to adhere to this implementation specification.

I do agree with @andy-ms' comment that async being allowed on an abstract method is a bug.

All 14 comments

Why is this necessary? The asterisk here is just syntactic sugar for the return type:

abstract class SomethingAbstract {
    abstract sadGenerator(): IterableIterator<any>;
}

If the extending class implements the method as generator or just returns an IterableIterator is an implementation detail.

That's true. However, I think the inconsistency inandofitself in the behavior is a problem: if we allow abstract generators, we should allow them to be written with generator syntax, sugar or not.

TypeScript does allow abstract async functions even though such declarations are also syntax sugar for a promise-returning abstract function (analogous to @ajafff's comment above). So it might be more consistent to allow abstract generators too.

We don't allow async in interfaces, type literals, or function types -- allowing async in an abstract class was probably an oversight?

abstract class C {
    async abstract m(): Promise<number>; // OK?
}

interface I {
    async m(): Promise<number>; // Error
}
type T = { async m(): Promise<number>; } // Error
type Cb = async () => Promise<number>; // Error

@andy-ms right, it looks like abstract async is the odd thing out here. If abstract async is made into an error, then tsc will be consistent across both async and generator functions, for abstract classes, interfaces, type literals and function types. All of them would reject async and * and require an appropriate return type annotation instead.

Out of curiosity, what's the rationale for not allowing async then on interfaces, etc? Is it just because it's redundant and sugar for the return type?

If the consensus is to make abstract async into an error for consistency (and continue to not allow * syntax for abstract generators), I'd like to try tackling that issue instead then 😄

I do not think the original issue is a bug. generators, just like async functions, are just an implementation detail. the contract does not care how the function is implemented but rather what parameters it takes, and what return type it offers. A generator function implements the Iterator protocol, but it can also be implemented using different means, and there is no reason derived classes has to adhere to this implementation specification.

I do agree with @andy-ms' comment that async being allowed on an abstract method is a bug.

@mhegazy Sounds good! Should I open an issue about async being allowed on abstract methods as a bug -- I would like to try fixing that.

sure.

How should this be implemented in a concrete class?

Currently,

abstract class A {
  public abstract m(): IterableIterator<string>;
}

class C implements A {
  public *m() {
    yield 'foo';
  }
}

Has the error, [ts] Non-abstract class 'C' does not implement inherited abstract member 'm' from class 'A'.

@dyst5422 I don't see any such error when trying to compile that example code in either [email protected] or typescript@next.

@andy-ms

I think this was a file reference issue. Unrelated

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

Is it possible to revive this issue by a suggestion that simply allow this ?
We of course can without any issues abstract async, but what about async* ?

abstract class A
{
    abstract async* getTransactions(data: Object): AsyncIterator<TransactionEntity[]>;
}

class B extends A
{
    async* getTransactions(data: Object): AsyncIterator<TransactionEntity[]>
    {

    }
}

Error:
TS1222: An overload signature cannot be declared as a generator.

I'm currently using a workaround with:

abstract class A
{
    async* getTransactions(data: Object): AsyncIterator<TransactionEntity[]>
    {
        throw new Error("Not implemented");
    }
}
Was this page helpful?
0 / 5 - 0 ratings