microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
99.86k stars 12.36k forks source link

Provide a way to type a fix-length tuple-like generator/iterator #42033

Open zojize opened 3 years ago

zojize commented 3 years ago

Search Terms

Suggestion

Express a fixed-length generator.

Use Cases

Examples

stackoverflow question

// Vector2.ts
class Vector2 {
  constructor (public x, public y) {
    this.x = x;
    this.y = y;
  }

  //... A bunch of vector methods

  public* [Symbol.iterator]: Generator<number, void, unknown> {
    yield this.x;
    yield this.y;
  }
}

// main.js
const canvas = document.querySelector('#canvas');
const ctx = canvas.getContext('2d');

const a = new Vector2(0, 0);
const b = new Vector2(10, 10);

ctx.beginPath();
ctx.moveTo(...a);
// works correctly, but with this warning: 
// Expected 2 arguments, but got 0 or more.ts(2556)
ctx.lineTo(...b);
ctx.stroke();
ctx.closePath();

Right now, the code above produces the correct behavior. However, typescript doesn't not recognize how many arguments is passed to ctx.moveTo and ctx.lineTo therefore it warns me about it.

My Implementation

There could be an tuple-like syntax for generators as well, for example:

public* [Symbol.iterator]: *[number, number] {
    yield this.x;
    yield this.y;
  }

With this syntax, typescript recognizes how many argument is deconstructed by the spread operator, and knows its a generator with the prepending *.

Checklist

My suggestion meets these guidelines:

treybrisbane commented 3 years ago

Wow, I just came here to raise this, only to find this issue created only a couple weeks ago! Crazy timing! 😮

In any case, the use-cases presented in #32523 also require (or rather, are partially enabled by) this feature. Specifically, those use-cases are:

treybrisbane commented 3 years ago

Thinking a bit about type inference...

I imagine it would be straightforward for simple cases, right?

For example, the inferred return type of

function* foo() {
  yield 0;
  yield 1;
}

would presumably be either *[0, 1] or *[number, number] (to borrow SIGUSR97's syntax).

As soon as loops are involved though, it looks like things get much more fun! 😅

For example, what would the inferred return type of

function* foo() {
  for (let n = 0; n < 5; n += 1) {
    yield n;
  }
}

be? I'm guessing something like *number[]?

Let's get more complex though. What would the inferred return type of

function* foo() {
  yield 0;

  for (let n = 1; n < 4; n += 1) {
    yield n;
  }

  yield 4;
}

be? Maybe something like *[0, ...*number[], 4] or *[number, ...*number[], number]?

When you start thinking about nested loops, things look a bit scary. Generics and conditionals further terrify me. 😱

treybrisbane commented 3 years ago

@rbuckton You seem to be Generator Man; got any thoughts on this? 😜

aleclarson commented 1 year ago

Can we get an official comment explaining why this isn't higher priority? I want to destructure a class instance as if it was a tuple. I define 0 and 1 getters on the prototype and both have different type signatures.

thehappycheese commented 2 months ago

Arrived here with the exact same use-case and issue as @zojize; vector types and the canvas.

Feels like I should be able to manually specify something like TupleGenerator<[number, number], ...> It could de-sugar to something like below;

class Vector2 {
  x:number;
  y:number;
  // public* [Symbol.iterator]: TupleGenerator<[number, number], void, unknown> { // <--desired syntax
  //   yield this.x;
  //   yield this.y;
  // }
  public* [Symbol.iterator]: Generator<number, void, unknown> { // de-sugared
    yield this.x;
    yield this.y;
  }
  public toTuple():[number, number]{ // de-sugared
    return [this.x, this.y]
  }
}

let a = new Vector2(1,2);
// ctx.moveTo(...a);  // <-- desired syntax
ctx.moveTo(...a.toTuple()); // de-sugared

Static type inference for *[Symbol.iterator] is obviously impossible for complex iterators... but why not have this for manually specified types :)

zojize commented 1 week ago

@thehappycheese I submitted a PR based on your suggestion, let me know what you think 😃