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

Iterator result of Iterable or AsyncIterable inferred to any #33932

Closed
ikokostya opened this issue Oct 10, 2019 · 4 comments
Closed

Iterator result of Iterable or AsyncIterable inferred to any #33932

ikokostya opened this issue Oct 10, 2019 · 4 comments
Assignees
Labels
Duplicate An existing issue was already created

Comments

@ikokostya
Copy link
Contributor

ikokostya commented Oct 10, 2019

TypeScript Version: 3.7.0-dev.20191010

Search Terms:

Iterable, AsyncIterable, Iterator, IteratorResult

Code

Synchronous iterators:

declare let syncIterable: Iterable<number>;
let res = syncIterable[Symbol.iterator]().next();
let value = res.value;

Asynchronous iterator:

declare let asyncIterable: AsyncIterable<number>;

(async () => {
    let res = await asyncIterable[Symbol.asyncIterator]().next();
    let value = res.value;
});

Expected behavior:

In both cases value has number type.

Actual behavior:

Variable value has any type.


Investigation

Type IteratorResult has two type parameters T and TReturn:

type IteratorResult<T, TReturn = any> = IteratorYieldResult<T> | IteratorReturnResult<TReturn>;

Because by default TReturn is any (interface Iterable has only one type parameter), we got number | any type, which is narrowed to any.

There are two problems:

  • There is no way to specify TReturn type parameter for IterableIterator and AsyncIterableIterator types.
  • Current behavior is unsafe.

Playground Link:

https://www.typescriptlang.org/play/index.html?ssl=3&ssc=23&pln=1&pc=1#code/CYUwxgNghgTiAEEQBd4GcCeA7MBJZIMUARkgFzz6ElIA8WArgLbGEB8A3AFBKpxrwAvOmx4CRUiADaAZQwsA9hAB0AS3FRkCmAF0AFAEplWEAA9kh7r3gA3KBAYJh-ZXYcgOQA

Related Issues:

#2983

@ikokostya
Copy link
Contributor Author

With explicit check done property of IteartorResult:

declare let syncIterable: Iterable<number>;
let res = syncIterable[Symbol.iterator]().next();
if (!res.done) {
    res.value // number
} else {
    res.value // any
}

@RyanCavanaugh RyanCavanaugh added the Needs Investigation This issue needs a team member to investigate its status. label Oct 17, 2019
@RyanCavanaugh RyanCavanaugh added this to the TypeScript 3.7.1 milestone Oct 17, 2019
@bterlson
Copy link
Member

bterlson commented Feb 8, 2020

In addition to also wanting the fix suggested above (plumbing TReturn and TNext from AsyncIterableIterator to IteratorResult), I wonder if any is the best type? When I see an any I don't suspect I need to narrow, I suspect I've done something wrong. undefined could be a better default, though I haven't considered this at length.

@rbuckton
Copy link
Member

This is a duplicate of 33353. Also, your expected behavior is incorrect. Without checking done, you cannot guarantee that value is a number:

const numbers: number[] = [];
const value = numers[Symbol.iterator]().next().value;
console.log(value); // undefined

While the correct result should be number | undefined, but may be a significant breaking change.

@rbuckton rbuckton added Duplicate An existing issue was already created and removed Needs Investigation This issue needs a team member to investigate its status. Rescheduled This issue was previously scheduled to an earlier milestone labels Dec 16, 2020
@ikokostya
Copy link
Contributor Author

Duplicate of #33353

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants