I got stuck trying to make TypeScript correctly infer an underlying iterable type, for function spread
below, which is supposed to take an iterable of iterable types, infer type of the underlying iterable and return a new iterable with that underlying type.
interface Output<T, R> {
(i: Iterable<T>): Iterable<R>;
}
function pipe<T, A>(i: Iterable<T>, p0: Output<T, A>): Iterable<A>;
function pipe(i: Iterable<any>, ...p: Output<any, any>[]): Iterable<any> {
return []; // dummy iterable
}
// I'm trying to implement this function:
function spread<T extends Iterable<R>, R>(): Output<T, R> {
return (iterable: Iterable<T>) => ({
[Symbol.iterator](): Iterator<R> {
return null as any; // dummy, for now
}
});
}
pipe(['text'], spread());
// expected types: spread => Output<string, string>, pipe => Iterable<string>
// actual types: spread => Iterable<string, unknown>, pipe => Iterable<unknown>
pipe([[1, 2], [3, 4]], spread());
// expected types: spread => Output<number[], number>, pipe => Iterable<number>
// actual types: spread => Output<number[], unknown>, pipe => Iterable<unknown>
In my mind, function spread
has the right template signature, but in the two examples above, the iterable type resolves into unknown
, which I cannot understand why.
The underlying spread
, in the meantime, infers into <T, unknown>
, and so pipe
fails to infer the type also.
This is for iter-ops library that I'm working on. I have implemented numerous operators, without problems, but got stuck with the spread
type inference.
UPDATE
Thanks to Titian Cernicova-Dragomir, I was able to properly finish the spread
operator :)
CodePudding user response:
If I understand the code correctly spread will transform an Iterable<Iterable<T>>
into and Iterable<T>
.
This means that the function returned by spread should have the signature (iterable: Iterable<Iterable<T>>) => Iterable<T>
. Using your types it should be Output<Iterable<T>, T>
. So there is no need for the R
argument as the input and output are related
interface Output<T, R> {
(i: Iterable<T>): Iterable<R>;
}
function pipe<T, A>(i: Iterable<T>, p0: Output<T, A>): Iterable<A>;
function pipe(i: Iterable<any>, ...p: Output<any, any>[]): Iterable<any> {
return []; // dummy iterable
}
function spread<T>(): Output<Iterable<T>, T> {
return (iterable: Iterable<Iterable<T>>) => ({
[Symbol.iterator](): Iterator<T> {
return null as any; // dummy, for now
}
});
}
let r = pipe(['text'], spread()); // Iterable<string>
let r2 = pipe([[1, 2], [3, 4]], spread()); // Iterable<number>