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

Allow generic yield* types #41646

Open
5 tasks done
Validark opened this issue Nov 23, 2020 · 1 comment
Open
5 tasks done

Allow generic yield* types #41646

Validark opened this issue Nov 23, 2020 · 1 comment
Assignees
Labels
Effort: Moderate Requires experience with the TypeScript codebase, but feasible. Harder than "Effort: Casual". Help Wanted You can do this Rescheduled This issue was previously scheduled to an earlier milestone Suggestion An idea for TypeScript

Comments

@Validark
Copy link

Validark commented Nov 23, 2020

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 #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.
@DanielRosenwasser DanielRosenwasser added the Bug A bug in TypeScript label Nov 23, 2020
@DanielRosenwasser DanielRosenwasser added this to the TypeScript 4.2.0 milestone Nov 23, 2020
@DanielRosenwasser DanielRosenwasser added Effort: Moderate Requires experience with the TypeScript codebase, but feasible. Harder than "Effort: Casual". Help Wanted You can do this labels Nov 23, 2020
@RyanCavanaugh RyanCavanaugh added the Rescheduled This issue was previously scheduled to an earlier milestone label Mar 4, 2021
@rbuckton
Copy link
Member

This is marked as a bug, but it's not clear to me what is being asked for here. The current type is consistent with what you would get if you did for..of, in that we don't try to construct a type that extracts the yielded type of T and instead give you the yielded type from the constraint:

for-of over a generic iterable
Playground

Are you asking that we introduce some type of IteratorNext<T>/IteratorReturn<T> types like we have with Awaited<T>? If so, I would classify this as more of a feature request than a bug.

@rbuckton rbuckton added Suggestion An idea for TypeScript and removed Bug A bug in TypeScript labels Jan 30, 2024
@RyanCavanaugh RyanCavanaugh removed this from the TypeScript 5.5.0 milestone Jul 26, 2024
@RyanCavanaugh RyanCavanaugh added this to the TypeScript 5.7.0 milestone Jul 26, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Effort: Moderate Requires experience with the TypeScript codebase, but feasible. Harder than "Effort: Casual". Help Wanted You can do this Rescheduled This issue was previously scheduled to an earlier milestone Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

5 participants