Repository: TypeScript
Issue: Incorrect generic type inference of generator function
Labels: Needs Investigation

### 🔎 Search Terms

- generator
- yield*

### 🕗 Version & Regression Information

This is the behavior in every version I tried, and I reviewed the FAQ for entries about generators.

### ⏯ Playground Link

https://www.typescriptlang.org/play?target=99&jsx=0#code/JYOwLgpgTgZghgYwgAgMoHsC2EA86CuYyAggHzIDeAUMsgG5wA2+EAXCQNxUC+VVYATwAOKAPJCwwdCBxlkAXjRZccgD7J8IACYQYoCFq5UE0gM5EYULAHEIIaHDDooC5LIA0yAEKkAFDWQAcwgwW3soR2d2XwBKBXIwhycoHHFJaVlSTy9PMncqGPY0qRkfeMoAkxBzILsk51dg0LqI5NijWkYQ5CgIU3xGIkVg8MioADp7AA8wdr5aAHcAC2Au5F8AQl7+wfGtaQg46lpaKpr0CRLXbYGwcYZmCC4T5GAYdYv0kAV5RU0dPT2LRxXpgfBQEDPE43QaNFpjSYQGa+T4le5MFgxKG8AKg8HfCj0DFsHp9W7ox7Ibhcal8GCaBBfABUyCciVazkyKMu0iKPJkZEKyHZY1S-MyuVy5GOyDORAeLFcAmAEEYWmQqOkULxEKJjxpfFAkFgiBQqHwACNTAgoMB+RVaEJGHAQOxzLaQIEoXYtOwACKOJ48Q3gaDwJDIACqpmgDuQIDg2DdYA9XoCEEwcFWydTUP6Vptdq+fK+OHNBdt-NIBqoOgQzt6srMRCaABl0IFgloAJIgaPQaJxeTkYoZftQat8OW1MDtzsGXvj8vWytfVyxEslMuWldFkrkYcBSw2eFtekgRklFmxOO0AD0d5eT+QAD03++3wF74-iFBAvhsHADV3kEERkAAchvYdhVPTkMGwbcKz3aRyHUeDcHHVCNG0XR9C0Txl0LflPHHZAADI0B3IivlIcCv2QB9XlMeN0CIOBTFMYBAgTC01icZAhDgCJsGNYDWWEFBIKHBJYJSUcZEI1d9wIqilOkFSkKrcDxno6d8BjFxFGVVUtBZNlZN8NsOy7RcDNiLFdObZB813e0jJVNUzPQEU2n06Bxhc6iSgcl4dW+QK1MhAJuAc4wnKs+cez7AzFOQkAACZ1yFeTENcmj4iPKxMB8zlSLQ1S0o0vL918c9L2ka8jnoxjnxOD92o6j9msfAAVCTZSWCAEAAa2YrRFX4sBBuQC1gAYW0XQjdAQOmgADaYwFW5ARKWdB1QtQhXiIUxdoGLQQHAsBupmoa4D85BVrKyjNK+LbgGYkBWOQdjOO4uBeJQfjHoM8jnuq6RVp0l49JB9yTK8kqoEskI5xs5LoHsqFTiciK0qVDzTNZbyLL8iZcf5ELoRCfFnIq-lsQcoA

### 💻 Code

```ts
interface Some<out A> {
  value: A;
}

type Option<A> = Some<A> | undefined;

const fromGenerator = <A, B>(
  getGenerator: () => Generator<Option<A>, B, A>,
): Option<B> => {
  const generator = getGenerator();

  let result = generator.next();

  while (!result.done) {
    const option = result.value;
    if (option === undefined) return;
    result = generator.next(option.value);
  }

  return { value: result.value };
};

function* toGenerator<A>(option: Option<A>): Generator<Option<A>, A, A> {
  const value = yield option;
  return value;
}

interface Subscription {
  plan: string;
  end: Date;
}

interface User {
  name: string;
  email: string;
  subscription: Option<Subscription>;
}

declare const getLoggedInUser: () => Option<User>;

const getLoggedInUserSubscription = (): Option<Subscription> =>
  fromGenerator(function* () {
    //          ^^^^^^^^
    // Argument of type '() => Generator<Some<Subscription> | Some<User> | undefined, Subscription, User & Subscription>'
    // is not assignable to parameter of type '() => Generator<Option<Subscription>, Subscription, Subscription>'.
    const user = yield* toGenerator(getLoggedInUser());
    const subscription = yield* toGenerator(user.subscription);
    return subscription;
  });

const getLoggedInUserSubscription2 = (): Option<Subscription> =>
  fromGenerator<User | Subscription, Subscription>(function* () {
    //          ^^^^^^^^^^^^^^^^^^^
    // Type checks due to the bivariance of the `next` method but it shouldn't
    // because `User | Subscription` is not assignable to `User & Subscription`.
    const user = yield* toGenerator(getLoggedInUser());
    const subscription = yield* toGenerator(user.subscription);
    return subscription;
  });
```


### 🙁 Actual behavior

The generator function provided to `fromGenerator` has the type `() => Generator<Some<Subscription> | Some<User> | undefined, Subscription, User & Subscription>`.

TypeScript is not able to infer the generic type `A` of function `fromGenerator` in function `getLoggedInUserSubscription`. However, if I explicitly provide the generic types, `fromGenerator<User | Subscription, Subscription>`, like in `getLoggedInUserSubscription2`, then it type checks. But, it should be noted that it only type checks due to the bivariance of the `next` method.

```
next(...args: [] | [TNext]): IteratorResult<T, TReturn>;
```

Without bivariance, `next(args_0: User & Subscription) => IteratorResult<Option<User | Subscription>, Subscription>` is not assignable to `next(args_0: User | Subscription) => IteratorResult<Option<User | Subscription>, Subscription>` because `User | Subscription` is not assignable to `User & Subscription`.

### 🙂 Expected behavior

Bivariance aside, it would be brilliant if TypeScript was able to infer that `A = User | Subscription` and `B = Subscription` without the need for explicit type annotations. Expecting the user of `fromGenerator` to provide explicit type annotations is a deal-breaker for me. So, the only other solution is to use `unknown` instead of generics.

```ts
const fromGenerator = <B>(
  getGenerator: () => Generator<Option<unknown>, B, unknown>,
): Option<B> => {
  const generator = getGenerator();

  let result = generator.next();

  while (!result.done) {
    const option = result.value;
    if (option === undefined) return;
    result = generator.next(option.value);
  }

  return { value: result.value };
};
```

However, this is not ideal because

1. It's less type safe. I can apply `generator.next` to any value. The type system doesn't restrict me from only applying `generator.next` to values of type `A`.
2. The generic type `A` serves as a form of documentation which tells us that `TYield` and `TNext` are related by the equation `TYield = Option<TNext>`. When we replace the generic type `A` with `unknown` then this relation is lost.


### Additional information about the issue

_No response_