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

More effective call arguments/prototype #43882

Closed
wants to merge 9 commits into from

Conversation

sandersn
Copy link
Member

Today, call resolution starts by expanding spread arguments with a tuple type like so:

f(string, [number, string], string) ==> f(string, number, string, string)

However, all other spread arguments are required to match a rest parameter. This PR adds two new expansions:

  1. Spreads of tuple unions expand into a union of each tuple member:
f(string, [string] | [number, number], string) ==> (f, string, number | string, number | undefined, string)
  1. Spreads of arrays expand into T | undefined for each member T, except for the last, which is T to match the required rest parameter:
declare function f(x,y,z,...rest)
f(string, string[], number) ==> f(string, string | undefined, string | undefined, string, number)

This is a prototype to see whether the idea works and discover what complications arise. The biggest assumption, which I still need to prove, is that even though the expansions don't cover all permutations of calls, they will be correct if and only if all permutations are correct.

For example, the first example

f(string, [string] | [number, number], string)

really has two possible expansions

f(string, string, string)
f(string, number, number, string)

and f(string, string | number, number | undefined, string) is only an approximation.

Because it's a prototype, it's missing a lot of pieces:

  • Array expansion isn't safe without strict null checks, because it essentially asserts that the array always has enough elements, instead of possibly having enough elements.
  • Neither expansion should happen when the target parameter is a rest, since that's already allowed.
  • Tuple union expansion only works with required and optional elements, but might possibly work with rest ones in trailing position.
  • The existing tuple expansion has special index elaborations that need to replace or augment the current new array expansion errors.
  • All of the above need a lot of tests.

Based on branch reform-arity-errors from #43855.

Fixes #42508

The second test actually requires node types
The two simple fixes, in arity error reporting, are in, and the
simplification of arity error reporting is half-done. I haven't started
on any improvements to call assignability.
And reword error a little.
1. Spreads of tuple unions expand into a union of each tuple member.
2. Spreads of arrays expand into T | undefined for each member T, except
for the last, which is T to match the required rest parameter.

This is a prototype to show that the idea works and discover what
complications arise. It's missing a lot of pieces:

- Array expansion isn't safe without strict null checks, because it
  essentially asserts that the array *always* has enough elements,
  instead of *possibly* having enough elements.
- Neither expansion should happen when the target parameter is a rest,
  since that's already allowed.
- Tuple union expansion only works with required and optional elements,
  but might possibly work with rest ones in trailing position.
- The existing tuple expansion has special index elaborations that need
  to replace or augment the current new array expansion errors.
- All of the above need a lot of tests.

Based on branch reform-arity-errors.
@sandersn
Copy link
Member Author

Thinking more about it, the tuple union expansion should use up post-tuple parameters for its too-short tuples instead of unconditionally inserting undefined.

@sandersn
Copy link
Member Author

To help with PR housekeeping, I'm going to close this draft PR. It is pretty old now.

@sandersn sandersn closed this May 25, 2022
@jakebailey jakebailey deleted the more-effective-call-arguments/prototype branch November 7, 2022 17:38
sandersn added a commit to sandersn/TypeScript that referenced this pull request Jul 28, 2023
Previously, only calls with tuples were allowed, not unions of
tuples. For example, this already works:

```
f(string, [number, string], string) ==> f(string, number, string, string)
```

But this does not:

```
f(string, [string] | [number, number]) ==> (f, string, string | number, number | undefined)
```

This PR allows union types like these *as the last argument*. It's
possible to allow them anywhere, but quite a bit more complicated. And
the code is already complicated enough: getEffectiveCallArguments now
needs to return the minimum number of arguments as well as the argument
list (which is the maximum number of arguments).

Also, this transformation should not happen when the tuple union
argument is passed to a tuple union rest parameter: `[number] |
[string]` is assignable to `[number] | [string]` but the transformed
`number | string` is not. Checking for this requires passing
around even more information.

Bottom line: I'm not sure the complexity is worth the new code.
However, if this is a good idea, there are 3 things that need to be cleaned up:

1. More precise error messages--maybe. Right now error messages are
reported in terms of the maximum number of arguments:
2. Allow tuples with trailing `...T` types. Not done or tested, but
straightfoward.
3. Find a more elegant way to return the minimum number of arguments.

Fixes microsoft#42508
Supercedes microsoft#43882, but does less than half of that PR, in less than half
the code.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Author: Team For Milestone Bug PRs that fix a bug with a specific milestone
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Error when spreading a union of tuples in a call
2 participants