Skip to content

Go to Definition assumes use of rest parameters when spread syntax is used with empty array unless of type [] #54977

Closed

Description

@aweebit

Bug Report

🔎 Search Terms

  • function declaration
  • function overload
  • rest parameters
  • spread syntax
  • Go to Source Definition

🕗 Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about spread parameters handling for overloaded functions.
  • Only tested in TS Playground with v5.1.6. The behavior is unlikely to have been different earlier.

⏯ Playground Link

Playground link with relevant code

💻 Code

function func(): void;
function func(...args: any[]): void;

function func(...args: any[]): void {}

func(...[]); // first declaration highlighted
func(...([] as any[])); // second declaration highlighted

🙁 Actual behavior

An array of type other than [] is assumed to always lead to the overload with rest parameters when Go to Definition is called, despite the fact that it can be empty.

🙂 Expected behavior

A list with both overloads opens up.

Activity

  1. changed the title [-]Go to Definition assumes se of rest parameters when spread syntax is used with empty array unless of type `[]`[/-] [+]Go to Definition assumes use of rest parameters when spread syntax is used with empty array unless of type `[]`[/+] on Jul 12, 2023
  2. Andarist commented on Jul 12, 2023

    @Andarist
    Contributor

    TypeScript might compute an "effective arguments list" at times when you are using spreads. You are effectively passing it 0 arguments with ...[] and that matches the first overload. An empty array is considered to be a 0-length tuple and that's not the same as any[].

    I think this works as expected.

  3. aweebit commented on Jul 12, 2023

    @aweebit
    Author

    It is perfectly fine that the first overload is chosen for the [] type. What I find problematic is the assumption that all arrays of type any[] are not empty. I used the empty array explicitly to illustrate the problem, but it could be any array whose contents we are not aware of, so either of the overloads could apply.

  4. Andarist commented on Jul 12, 2023

    @Andarist
    Contributor

    In TS only a single overload can be matched anyway (from the top to the bottom) so without even going into how TS treats array I still think this is OK.

  5. aweebit commented on Jul 12, 2023

    @aweebit
    Author

    It is not about overload matching for a particular function call with particular parameter values, it is about the Go to Definition feature which, as far as I can tell, only works with symbols and not values. Try Ctrl + clicking the second function call in the playground and you'll see the second declaration highlighted, but instead, a list with both declarations should open up.

  6. Andarist commented on Jul 12, 2023

    @Andarist
    Contributor

    Ye, I know what you are saying here - but the fact is that Go to Definition actually goes to the resolved signature (can also be tested using a different demo here). This behavior is actually quite helpful because you can easily jump to the matched overload - and if you need to look at others they usually are just close to it.

  7. fatcerberus commented on Jul 12, 2023

    @fatcerberus

    The spread/rest is IMO a red herring here. This is analogous to writing:

    declare function foo(x: string): void;
    declare function foo(x: string | number): void;
    declare const bar: string | number;
    foo(bar);  // go to def picks second overload

    Yes, sometimes it will match the first overload "at runtime" (ignoring the fact that overloads are pure type info and don't exist at runtime, except insofar as they are encoded in the single function implementation), just as an any[] might sometimes be [] at runtime, but the call only resolves to the string | number overload regardless. Same principle.

  8. aweebit commented on Jul 12, 2023

    @aweebit
    Author

    Thanks to both of you for clearing up my misconceptions!

  9. locked as resolved and limited conversation to collaborators on Oct 22, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions