`consume` types and `AnyIterable`
achingbrain opened this issue · 1 comments
achingbrain commented
It doesn't look like I can use the AnyIterable
type with consume
, even though it supports both Iterable<T>
and AsyncIterable<T>
as input:
import { consume } from 'streaming-iterables'
import type { AnyIterable } from 'streaming-iterables'
// specify return type otherwise typescript works out this is really `number[]`
function createSyncSource (): AnyIterable<number> {
return [1, 2, 3]
}
// specify return type otherwise typescript works out this is really `AsyncGenerator<number, void, undefined>`
async function * createAsyncSource (): AnyIterable<number> {
yield * [1, 2, 3]
}
const sourceArr = createSyncSource()
const sourceGen = createAsyncSource()
// works if I cast to the underlying type
consume(sourceArr as number[])
consume(sourceGen as AsyncIterable<number>)
// does not select the correct overload based on the possible types of input
consume(sourceArr)
consume(sourceGen)
The error is:
error TS2769: No overload matches this call.
Overload 1 of 2, '(iterable: Iterable<number>): void', gave the following error.
Argument of type 'AsyncIterable<number>' is not assignable to parameter of type 'Iterable<number>'.
Property '[Symbol.iterator]' is missing in type 'AsyncIterable<number>' but required in type 'Iterable<number>'.
Overload 2 of 2, '(iterable: AsyncIterable<number>): Promise<void>', gave the following error.
Argument of type 'AsyncIterable<number>' is not assignable to parameter of type 'AsyncIterable<number>'.
Property '[Symbol.asyncIterator]' is missing in type 'Iterable<number>' but required in type 'AsyncIterable<number>'.
If I add an additional overload to the type definition that's the same as the actual implementation it starts to work:
export function consume<T>(iterable: Iterable<T>): void
export function consume<T>(iterable: AsyncIterable<T>): Promise<void>
export function consume<T>(iterable: AnyIterable<T>): Promise<void> | void // <-- new overload
export function consume<T>(iterable: AnyIterable<T>) {
if (iterable[Symbol.asyncIterator]) {
return _consume(iterable)
}
for (const val of iterable as Iterable<T>) {
// do nothing
}
}
But then the return type is Promise<void> | <void>
. I tried doing something clever like:
export declare type UnwrapToVoidOrVoidPromise<M extends AnyIterable<any>> = M extends Iterable<any> ? void : M extends AsyncIterable<any> ? Promise<void> : never;
but it's still Promise<void> | <void>
. Maybe that's ok, I'm not sure.
reconbot commented
I'm not 100% sure what to do here. I think adding void would be fine, I'd be for a pr.