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

Preserve ordered yield types when no logic is involved #43150

Open
5 tasks done
sdegutis opened this issue Mar 8, 2021 · 7 comments
Open
5 tasks done

Preserve ordered yield types when no logic is involved #43150

sdegutis opened this issue Mar 8, 2021 · 7 comments
Labels
Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript

Comments

@sdegutis
Copy link

sdegutis commented Mar 8, 2021

Suggestion

πŸ” Search Terms

yield ordered return types

βœ… Viability 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, new syntax sugar for JS, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.

⭐ Suggestion

Make types more definite for each yielded return value in a generator function

See also https://stackoverflow.com/questions/66538431/function-chain-factory-in-typescript-preserving-input-output-types

πŸ“ƒ Motivating Example

Right now, if I do this:

function* work() {
  yield 1;
  yield 'test';
  return [123];
}

Right now this gives:

const g = work();
g.next().value  // string | number | number[]
g.next().value  // string | number | number[]
g.next().value  // string | number | number[]

But since there's no logic/branching/etc, it would be more helpful to me to have:

const g = work();
g.next().value  // number
g.next().value  // string
g.next().value  // number[]

πŸ’» Use Cases

Sometimes I want to use a function generator to represent a series of steps that I want to examine (whether at runtime or during testing, etc) before deciding whether to continue. In other words, I want to break up a function into a series of "pauseable" steps.

@RyanCavanaugh RyanCavanaugh added Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript labels Mar 9, 2021
@jcalz
Copy link
Contributor

jcalz commented Mar 9, 2021

Is this like #32523 and/or #42033?

@RyanCavanaugh
Copy link
Member

#42033 looks very close, yes.

@sdegutis
Copy link
Author

sdegutis commented Mar 9, 2021

@RyanCavanaugh I think this one could be a very simple "gateway" implementation for other issues like that one which cover more cases. This might be just enough of a catalyst to justify making the minimal changes to the type system needed to get the ball rolling for other features.

@sdegutis
Copy link
Author

sdegutis commented Mar 9, 2021

@RyanCavanaugh The reason I think so btw is because I saw in #32523 (comment) that it's the missing relationship between TYield and TNext that's the show-stopper, and this feature request is just for the specific use-case when you can clearly see that there's no yields inside any branching/logic, so that you can safely know that it's just a fixed list of types. It might even be possible to use tuple types to achieve this. At which point, the groundwork is laid for the more complex use-cases. Anyway just an idea.

@sdegutis
Copy link
Author

sdegutis commented Mar 9, 2021

Oh. Looks like that issue was talking about the type of the value returned to the yield, not the value returned from the yield. Oops.

@sdegutis
Copy link
Author

sdegutis commented Aug 9, 2024

Honestly I'm not even sure what the use-case is for this anymore. I certainly don't currently have one. Worth closing imo.

@iliaamiri
Copy link

@thesoftwarephilosopher

My use-case is that I want to create a minimal array that only has the tuple values and no other methods such as: map, flatMap, forEach, etc.

export type MinimalArray<T, E> = {
  0: E
  1: T
  length: 2
  [Symbol.iterator](): Iterator<E | T> // I can't pass the tuple values here. It will make them a union type. 
}

Let's say I created an object and artificially implemented the [Symbol.iterator]() method myself.

class CustomTuple<T1, T2> {
  private readonly _values: [T1, T2]; // Internal array-like storage for elements

  constructor(first: T1, second: T2) {
    this._values = [first, second];

    // Define properties 0 and 1 to enable indexed access (like a tuple)
    Object.defineProperty(this, '0', {
      value: first,
      enumerable: false,
      writable: false,
    });

    Object.defineProperty(this, '1', {
      value: second,
      enumerable: false,
      writable: false,
    });
  }

  // Length property to match tuple behavior
  length: 2 = 2;

  // Iterator for spreading and iteration
  [Symbol.iterator](): Iterator<T1 | T2> {
    let index = 0;
    return {
      next: (): IteratorResult<T1 | T2> => {
        if (index < this._values.length) {
          return { value: this._values[index++], done: false };
        } else {
          return { value: undefined, done: true };
        }
      },
    };
  }
}

When I destructure it, I get a union type:

const tuple = new CustomTuple(1, "bar")

const [a, b] = tuple
// a -> string | number
// b -> string | number

The nice thing about my CustomTuple is that, I don't see the extra Array API that comes with a regular Array object:
image

So the user cannot do tuple[0] or tuple[1] either. That's what I want.


So, there is no way I can achieve the same result as defining a standard fixed tuple:

const foo = [1, "bar"]

const [a, b] = foo
// a -> number
// b -> string

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

4 participants