microsoft / microsoft/TypeScript

Rest parameter in callback function using generic tuple types: Forces definition of all parameters

Open
#35,154 3 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Needs Investigation
Dominant language
Go
Stars
111k
Forks
14.3k
Avg merge
2d 4h
Merged PRs (30d)
132

Description

TypeScript Version: 3.8.0-dev.20191116

Search Terms:
rest parameter, spread, tuple type, generics, conditional types

Code
https://github.com/BTOdell/typescript-tuple-rest-spread-bug

This issue was discovered when implementing a type-safe event emitter class. I've produced a minimum reproducible example at the GitHub link above as well as in a Playground link below.

  • index.ts defines an unimplemented EventEmitter that includes 4 functions which should be equivalent (except for the eventName parameter).
  • test.ts consumes the EventEmitter class and calls the various functions to test type-safety.

An Events interface is used to define the supported events by mapping the event name to a tuple type for the parameters of the event listener.
This interface is passed through a generic parameter E in the Test class and then passed through a generic parameter E in the EventEmitter class.

Expected behavior:
A listener function should be able to define 1 parameter even if the caller might pass 2 parameters. The listener will then only have access to the first parameter passed. This is how JavaScript normally behaves.

Actual behavior:
The unexpected behavior occurs on lines 29 and 33 (in test.ts) and only occurs when the Events interface type has to pass through 2 generic class parameters.
If line 14 is uncommented, and line 13 is commented then all unexpected behavior is resolved.

Error:(29, 47) TS2345: Argument of type '(b: boolean) => void' is not assignable to parameter of type '(...args: Args<E["multiArray"]>) => any'.
  Types of parameters 'b' and 'args' are incompatible.
    Type 'Args<E["multiArray"]>' is not assignable to type '[boolean]'.
      Type 'E["multiArray"] | [E["multiArray"]]' is not assignable to type '[boolean]'.
        Type 'any[] & E["multiArray"]' is not assignable to type '[boolean]'.
          Types of property 'length' are incompatible.
            Type '2' is not assignable to type '1'.
              Type '[boolean, boolean]' is not assignable to type '[boolean]'.
                Types of property 'length' are incompatible.
                  Type '2' is not assignable to type '1'.

It seems like the tuple types [boolean, boolean] and [boolean] are not being treated like they're being spread as a rest parameter in a function. Could the context information be getting lost due to jumping through multiple generic parameters?

Playground Link:
Playground link

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reproducing the errors on lines 29 and 33 of test.ts in the linked minimum reproducible example, then inspect the related definitions in index.ts. Confirm the expected behavior by checking that a one-parameter listener is accepted for a two-parameter tuple while type safety remains intact.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.