Skip to content

Typescript Autocompletion Not Working For Function Overloads #26892

Open

Description

@mjbvz

From Martin Löper (@MartinLoeper) on August 29, 2018 13:27

  • VSCode Version: 1.25.1 1dfc5e557209371715f655691b1235b6b26a06be x64
  • OS Version: Linux version 4.15.0-32-generic Ubuntu

Steps to Reproduce:
I provided a small example of the issue (see screenshot below).

  1. Create a function with two overloads
  2. Try to invoke this function
  3. Once you typed in the first parameter, the IDE recognizes that it matches the first overload's signature.
    However, the auto-completion suggest the literals for both overloads.

I expect the auto-completion to show "BW" and "BY" only!

_005

Does this issue occur when all extensions are disabled?: Yes

The code:

export class MomentHolidayFactory {
    public a(input: Germany, second: GermanState): string;
    public a(input: Austria, second: AustrianState): string;

    public a(input: Country, second: State): string {
        return "test";
    }

    public holidays() {
        this.a("de", ""
    }
}

type Country = Germany & Austria;
type Germany = "de" | "De" | "DE" | "germany";
type Austria = "Au" | "au" | "AU" | "austria";

type State = GermanState & AustrianState;
type GermanState = "BW" | "BY";
type AustrianState = "Stmk" | "Vbg";

Copied from original issue: microsoft/vscode#57509

Activity

  1. removed their assignment
    on Sep 5, 2018
  2. ChuckJonas commented on Nov 8, 2018

    @ChuckJonas

    I'm also having an issue with autocomplete on overloaded code:

        public select<P extends NonReferenceNonFunctionPropNames<T>>(f: P): string;
        public select<P extends NonReferenceNonFunctionPropNames<T>>(f: FunctionField<T>): string;
        public select<P extends NonReferenceNonFunctionPropNames<T>>(f: P[]): string[];
        public select<P extends NonReferenceNonFunctionPropNames<T>>(f: P | P[] | FunctionField<T>): string | string[] {
            let relations = this._traversed.map(r => r.apiName);
            if (Array.isArray(f)) {
                let fieldArr: string[];
                fieldArr = f.map(field => {
                    return this._obj.FIELDS[field as string].apiName
                });
                return fieldArr.map(field => {
                    return [...relations, ...[field]].join('.');
                });
            } else if (typeof f === 'object') {
                let apiName = this._obj.FIELDS[f.field as string].apiName
                return renderComplexTypeText([...relations, ...[apiName]].join('.'), f.func, f.alias)
            } else {
                return [...relations, ...[this._obj.FIELDS[f as string].apiName]].join('.');
            }
        }

    The auto-compete does work on the instances with P instances, but does NOT work when calling the P[] or FunctionField<T> (typechecking does still work, but it's very hard to remember these key names)

  3. Kingwl commented on Dec 28, 2018

    @Kingwl
    Contributor

    What is the expected behavior?
    implement a partial signature resolve or fill parameters with correct type?

  4. fuafa commented on Feb 18, 2019

    @fuafa
    Contributor

    The signature help is wrong either if the first argument is Austria, the signature help still shows the first overload.

  5. jueinin commented on Nov 18, 2020

    @jueinin

    any updates on this? I have a function with 100+ overloads. needs this feature very much!

  6. Kingwl commented on Nov 18, 2020

    @Kingwl
    Contributor

    I have a function with 100+ overloads.

    Good luck....

  7. jueinin commented on Nov 18, 2020

    @jueinin

    I have a function with 100+ overloads.

    Good luck....

    Wenlu Wang (@Kingwl)
    sad...
    I write 100+ overloads in one go
    and finally, i find that the autocomplete has been totally destroyed.

  8. dani-ras commented on Oct 6, 2022

    @dani-ras

    Is there any update on this?
    I just tried using an overloaded function and found the autocomplete is not narrowing after providing partial argument..

  9. ethanresnick commented on May 18, 2023

    @ethanresnick
    Contributor

    Ryan Cavanaugh (@RyanCavanaugh) I'd like to help with this, as fixing it would enable a library API design that isn't really viable right now, while autocomplete is suggesting invalid candidates. However, I've never contributed to the language server before and don't know how it's set up. Do you have any sense of how hard this would be? And, if not too bad, any pointers on how to get started?

  10. dwjohnston commented on Oct 9, 2024

    @dwjohnston

    Here is another simple example of this behaviour:

    type UserTypes = {
     userType: "admin" | "user" | "unauthorized"; 
    }
    
    
    type SessionData = {
      sessionId: string; 
      userData?: {
        username: string; 
      }
    }
    
    type AuthedSessionData = Required<SessionData>;
    function getUserData(userType: {userType: "unauthorized"}) : SessionData 
    function getUserData(userType: {userType: "admin" | "user" }) : AuthedSessionData 
    function getUserData(userType: {userType: "unauthorized"} | {userType: "admin" | "user"}) : SessionData | AuthedSessionData {
      if(userType.userType === "unauthorized"){
        return {
          sessionId: "123"
        }
      }
    
      return {
        sessionId: "123", 
        userData: {
          username: "foo"
        }
      }
    }
    
    
    
    const result1 = getUserData({"userType": "unauthorized"})
    const result2 = getUserData({"userType": "admin"}); // <-- Valid, but no autocomplete 

    We can get the autocomplete behaviour back by converting to a generic:

    type UserTypes<T extends "admin" | "user" | "unauthorized">  = {
     userType: T ; 
    }
    
    
    type SessionData = {
      sessionId: string; 
      userData?: {
        username: string; 
      }
    }
    
    type AuthedSessionData = Required<SessionData>;
    
    function getUserData<T extends "admin" | "user" | "unauthorized"> (userType: UserTypes<T>) : T extends "unauthorized" ? SessionData : AuthedSessionData {
      if(userType.userType === "unauthorized"){
        return {
          sessionId: "123"
        } as T extends "unauthorized" ? SessionData : AuthedSessionData
      }
    
      return {
        sessionId: "123", 
        userData: {
          username: "foo"
        }
      }
    }
    
    
    
    const result1 = getUserData({"userType": "unauthorized"})
    const result2 = getUserData({"userType": "admin"});

    But as a rule of thumb, I prefer to use function overloads over generics - tends to be simpler.

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

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions