Effort: ModerateHelp WantedRescheduledSuggestion
Description
Search Terms
generic yield asterisk type preserve
Suggestion
Currently, the yield* construct doesn't work properly with generic types. I would like to see these kinds of situations get proper types:
function* foo<T extends Iterable<any>>(x: T) {
yield* x;
}
// Inferred return type: `Generator<any, void, undefined>`
I would like to see some kind of Iteratorify types added:
type YieldTypeOfIterableType<R> = R extends Iterable<infer Y> | Iterator<infer Y> | IterableIterator<infer Y> | Generator<infer Y> ? Y : never;
type Awaited<T> =
T extends null | undefined ? T : // for non-strictNullChecks
T extends PromiseLike<infer U> ? Awaited<U> :
T;
/** Extracts the `Symbol.asyncIterator` return type, falling back to the `Symbol.iterator` return type if necessary. */
type AsyncIteratorify<T> = [T] extends [{ [Symbol.asyncIterator]: () => void } | { [Symbol.iterator]: () => void }]
// Tests whether a given type has a valid AsyncIterator / Iterator in every unioned type before actually computing the type
// This is so AsyncIteratorify<Set<string> | {}> will resolve to `never`
? T extends infer U
? U extends { [Symbol.asyncIterator]: () => infer R }
? YieldTypeOfIterableType<R>
: U extends { [Symbol.iterator]: () => infer R }
? Awaited<YieldTypeOfIterableType<R>>
: never
: never
: never;
/** Extracts the `Symbol.iterator` return type. */
type Iteratorify<T> = [T] extends [{ [Symbol.iterator]: () => void }]
// Tests whether a given type has a valid Iterator in every unioned type before actually computing the type
// This is so Iteratorify<Set<string> | {}> will resolve to `never`
? T extends infer U
? U extends { [Symbol.iterator]: () => infer R }
? YieldTypeOfIterableType<R>
: never
: never
: never;
function foo<T extends Iterable<any>>(arr: T): Generator<Iteratorify<T>>;
function* foo<T extends Iterable<any>>(x: T) {
yield* x;
}
for (const x of foo([1, 2])) console.log(x); // x is a number :D
However, this still won't work for generic this parameters in Symbol.iterator calls:
for (const x of foo({
a: 1,
*[Symbol.iterator]<T>(this: T) {
for (const x in this) yield x;
}
})) console.log(x);
For this, we would need https://github.com/microsoft/TypeScript/issues/37181 (and we would need a way to allow for passing in implicit this parameters in that proposal)
Checklist
My suggestion meets these guidelines:
- This wouldn't be a breaking change in existing TypeScript/JavaScript code
- This wouldn't change the runtime behavior of existing JavaScript code
- This could be implemented without emitting different JS based on the types of the expressions
- This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
- This feature would agree with the rest of TypeScript's Design Goals.