Tslint: [Bug] Does not detect class as implementing AsyncIterable.

Created on 27 Jun 2018  ยท  7Comments  ยท  Source: palantir/tslint

Bug Report

  • __TSLint version__: 5.10.0
  • __TypeScript version__: 2.8.3
  • __Running TSLint via__: (pick one) CLI

TypeScript code being linted

class Stream implements AsyncIterable<number> {
  [Symbol.asyncIterator](): AsyncIterator<number> {
      return null as any;
  }
}
const stream: AsyncIterable<number> = new Stream();
const streamTyped: Stream = new Stream();

async function test() {
  // Gives no linter error.
  for await (const feature of stream) {
    // Do something
  }
  // Gives a linter error, await-promise
  for await (const feature of streamTyped) {
    // Do something
  }
}

with tslint.json configuration:

{
    "defaultSeverity": "error",
    "extends": [
        "tslint:recommended"
    ],
    "jsRules": {},
    "rules": {
        "await-promise": true
    },
    "rulesDirectory": []
}

with tsconfig.json:

{
  "compilerOptions": {
    "target": "es2017",                          
    "module": "commonjs",                     
    "lib": ["esnext.asynciterable", "es2017"],                          
    "strict": true,                    
    "esModuleInterop": true,       
  }
}

Actual behavior

ERROR: /test.ts[15, 31]: Invalid 'for-await-of' of a non-AsyncIterable value.

Expected behavior

No linting error.

Bug ๐ŸŒน R.I.P. ๐ŸŒน

Most helpful comment

@ajafff any chance of porting your improvements back to tslint?

All 7 comments

That's because the rule only recognises AsyncIterableIterator<T> which is a mistake I made when implementing it back then. This should be AsyncIterable<T> instead / in addition.

But there's more:

  • it should allow every value that implements the async iteration protocol
  • it should allow Iterable<PromiseLike<T>>. This is the fun one to implement ๐Ÿ˜„
  • the part of the rule checking await should allow everything that implements the Promise protocol instead of relying on names

I implemented all of these cases for my own linter runtime Fimbullinter. It's called await-only-promise and is basically the better version of TSLint's await-promise.

@ajafff any chance of porting your improvements back to tslint?

I ran into this issue today.

Any workaround right now?

Same problem with NodeJS.ReadableStream, which implements [Symbol.asyncIterator](): AsyncIterableIterator<string | Buffer> (without an explicit implements clause)

๐Ÿ’€ _It's time!_ ๐Ÿ’€

TSLint is deprecated and no longer accepting pull requests other than security fixes. See #4534. โ˜ ๏ธ
We recommend you instead use typescript-eslint to lint your TypeScript code with ESLint. โœ…

๐Ÿ‘‹ It was a pleasure open sourcing with you!

interface IDog {
  breed: string;
  age: number;
}

async function getDog(id: number): Promise<IDog> {
  const dogs: IDog[] = [
    { breed: "german shepherd", age: 2 },
    { breed: "labrador", age: 4 }
  ];
  return dogs[id] || undefined;
}

const dogIterable: AsyncIterable<IDog> = {
  [Symbol.asyncIterator](): AsyncIterator<IDog, undefined> {
    let i = 0;
    return {
      async next(): Promise<IteratorResult<IDog>> {
        const dog = await getDog(i);
        i++;
        return {
          done: dog === undefined,
          value: dog
        };
      }
    };
  }
};

async function showDogs(): Promise<void> {
  for await (const dog of dogIterable) {
    console.log(dog);
  }
}

showDogs().catch( e=> { throw new Error(e) });

๐Ÿค– Beep boop! ๐Ÿ‘‰ TSLint is deprecated ๐Ÿ‘ˆ _(#4534)_ and you should switch to typescript-eslint! ๐Ÿค–

๐Ÿ”’ This issue is being locked to prevent further unnecessary discussions. Thank you! ๐Ÿ‘‹

Was this page helpful?
0 / 5 - 0 ratings