Repository: TypeScript
Issue: Spread operator should not have worse ergonomics than `apply` - unexpected error spreading a union-of-tuples
Labels: Suggestion,Experimentation Needed

# Bug Report
Attempting to spread a union-of-tuples that is otherwise compatible with the function being called results in an unexpected compiler error:

**4.3.5+** "A spread argument must either have a tuple type or be passed to a rest parameter."
**3.33-4.2.3** "Expected 2 arguments, but got 0 or more."

However, calling `apply` on the function with the same union-of-tuples does not produce an error, and neither does calling the function "memberwise" using indexes into the union-of-tuples. In both the `apply` case and the memberwise case, the user is getting the benefit of some level of typechecking, whereas when using the spread operator, the user is met with a hard error.

The error message is confusing, but more confusing is why there would even be an error. I am opening this issue primarily to address the error, not the message.

Since there is a very clear relationship between `apply`, a memberwise call, and the spread operator, it is perplexing why spread does not work in this case. I imagine that hitting this kind of error might make JS switchers to TS quite confused and possibly cause them to abandon ship. That same relationship between spread (which does not work as expected) and `apply` (which works adequately) also points to at least one possible path to implementing the desired behavior of successfully typechecking a spread involving a union-of-tuples.

<!--
  Please fill in each section completely. Thank you!
-->

### 🔎 Search Terms
spread, apply, union of tuples, A spread argument must either have a tuple type or be passed to a rest parameter
<!--
  What search terms did you use when trying to find an existing bug report?
  List them here so people in the future can find this one more easily.
-->

### 🕗 Version & Regression Information

<!-- When did you start seeing this bug occur?

"Bugs" that have existed in TS for a long time are very likely to be FAQs; refer to
  https://github.com/Microsoft/TypeScript/wiki/FAQ#common-bugs-that-arent-bugs

If possible, please try testing the nightly version of TS to see if it's already been fixed.
For npm: `typescript@next`
This is also the 'Nightly' version in the playground: http://www.typescriptlang.org/play/?ts=Nightly

Note: The TypeScript Playground can be used to try older versions of TypeScript.

Please keep and fill in the line that best applies:
-->
- This is the behavior in every version I tried, and I reviewed the FAQ for entries about spread

### ⏯ Playground Link
[Playground link with relevant code](https://www.typescriptlang.org/play?#code/PTAEEFQMwVwOwMYBcCWB7OokAsCGStcBrAUwGcsB3NUXAJwHMYBbEuJMgLgChZFUM0OAApcnUGSR0UcBqAA+oOCwBGJOgBpQK8ZOmyFS1eoCUoAN7dQ10Agxk0AGxIA6R2gaitKkwG5uAL7c3CAQoPDocAC0aFBRAExRSDAADs4USACeKSTcWTmgACqgALygANp6MgxaVbIAuoblysxqmkat6vX+IWCQERgxcclp5KAAbvQouCrO3M4EBOLFZQCy+NgudLhwACZozMJmAHygAAwuAKygAPwVAETg91r3AEL3jeLlAIxa393BUIAYR2oEoJFsuEcjiw2AhUEwrE6dCilBQZAhaJwtHCcEiQySqXSdwAmuQXLwREhymd6lpqf8-ICwCDMODIdDYfD4MhIrC6GgYAxsLQUmlMmCUNjcLj8bFCaMyKTyZSXLgxY5MsJlND6UzeqAAHJoJAQnD4UAybQmkUIXAYigzNDjM3ZCEIOEIIjVS0UPBitjVACEoGZoFZYIhZBSdBIuF2XImUxmzjuhoA8kHKcIXLmkH5QKEAKIAJRL6ZL4kg0dj8dojBYbAIzBgklAJClcLooDwLpxI2cWDdoDQ3bUoBS9oxCaQNBlsbbk+2rFNdApQA)
<!--
  A link to a TypeScript Playground "Share" link which shows this behavior

  The TypeScript Workbench can be used for more complex setups, try
  https://www.typescriptlang.org/dev/bug-workbench/

  As a last resort, you can link to a repo, but these will be slower for us to investigate.
-->

### 💻 Code

<!-- Please post the relevant code sample here as well-->
```
// A function that takes two arguments:
function fn(a: string | number, b: string | number) {
    console.log(a, b);
}

// A union-of-2-tuples type
type T = [string, string] | [number, number];

// A union-of-tuples variable
let t : T = Math.random() > 0.5 ? ["A", "B"] : [1, 1];

// Can we call the fn member-wise with a union-of-tuples? Yes.
fn(t[0], t[1]);

// Can we call the function through apply with a union-of-tuples? Yes.
fn.apply(null, t);

// Note that in both cases above type checking is happening! 

// Can we spread the variable? No.
fn(...t); // ERROR: A spread argument must either have a tuple type or be passed to a rest parameter.
```


### 🙁 Actual behavior

<!-- What happened, and why it was wrong -->
In the code above, you can see that we have a variable `t` that is a union-of-tuples.

Each tuple in the union is type-compatible with the function `fn`.

You can see that we are able to call the function using the members of `t` without any casts nor compiler errors: `fn(t[0], t[1])`.

You can also see that we are able to pass `t` to the function in one go using `apply`: `fn.apply(null, t)`. Again, this is successful and has no casts or other code artifacts.

Finally, we attempt to call the function by spreading `t`: `fn(...t)`. This alone causes the compiler to produce an error.  

### 🙂 Expected behavior

<!-- What you expected to happen instead, and why -->
I would expect the code using the spread operator, `fn(...t)`, to succeed, just as the `apply` and memberwise versions succeeded.