Skip to content

VSCode IntelliSense not suggesting methods from dynamic type or interface generated with template literals #62689

@rodriguezrrp

Description

@rodriguezrrp

🔎 Search Terms

"Intellisense template literal keys", "VSCode Typescript Intellisense dynamic type", "VSCode Typescript Intellisense dynamic interface", "Intellisense interface suggestion", "Intellisense dynamic type suggestion", "Intellisense template literal dynamic key type", "Intellisense template literal dynamic key interface"

🕗 Version & Regression Information

  • This is the behavior in every version I tried (the latest two), and I reviewed the FAQ for entries about Intellisense, template, and literal.

⏯ Playground Link

Playground link

💻 Code

type EventType = 'start' | 'foo' | 'bar' | 'fooBar' | 'end';

type EventDataType<T extends EventType> =
    T extends 'fooBar' ? { foo: string, bar: number }
    : T extends 'foo' ? { foo: string }
    : T extends 'bar' ? { bar: number }
    : T extends EventType ? Record<string, any>
    : never;

type ListenerFunction<T extends EventType> = (context: any, data: EventDataType<T>) => void | Promise<void>;

type EvtListenerMethods = {
    [K in EventType as `on${Capitalize<K>}`]?: ListenerFunction<K>;
};

/*
* Dynamic solution attempt A: directly implement the generated type.
* Type checking works, but Intellisense does not suggest the methods.
*
* Note A.onBar will raise a type error, as expected
*/

class A implements EvtListenerMethods {
    onFoo(context: any, data: EventDataType<'foo'>) { /*...*/ };
    onBar(context: any, data: { abcd: any }) { /*...*/ };
}

/*
* Dynamic solution attempt B: implement an interface that extends the generated type.
* Type checking works, but Intellisense does not suggest the methods.
* 
* Note B.onBar will raise a type error, as expected
*/

interface EvtListener extends EvtListenerMethods {
}

class B implements EvtListener {
    onFoo(context: any, data: EventDataType<'foo'>) { /*...*/ };
    onBar(context: any, data: { abcd: any }) { /*...*/ };
}

/*
* Hardcoded solution C: write out every method and its signature.
* Works both with type checking and with Intellisense suggestions.
* 
* Note C.onBar will raise a type error as expected
*/

interface EvtListenerHardcoded {
    onStart?: ListenerFunction<"start">;
    onFooBar?: ListenerFunction<"fooBar">;
    onFoo?: ListenerFunction<"foo">;
    onBar?: ListenerFunction<"bar">;
    onEnd?: ListenerFunction<"end">;
}

class C implements EvtListenerHardcoded {
    onFoo(context: any, data: EventDataType<'foo'>) { /*...*/ };
    onBar(context: any, data: { abcd: any }) { /*...*/ };
}

🙁 Actual behavior

VSCode IntelliSense does not recognize/suggest function names on the dynamically generated type EvtListenerMethods or the extending interface EvtListener (from template literals on the original EventType constant string union). It only does so with the hardcoded interface, which should not be functionally different.
For what it's worth, changing methods to required (removing optional specifier ?) does not change this behavior.

All expanded function names and signatures are knowable at compile time, i.e., type-checking time. Type checking works, raising errors on incorrect signatures of implementations of said methods, and when missing any required methods.

Screenshot of current IntelliSense suggestions on an implementer of the dynamic type/interface:

Image

🙂 Expected behavior

VSCode IntelliSense should recognize/suggest the dynamically generated function names (from template literals on the original EventType constant string union) just as it does when all the functions are written out individually in a type/interface.

Screenshot of current IntelliSense suggestions on an implementer of the hardcoded interface, which is expected for dynamic type/interface as well:

Image

Additional information about the issue

This issue was prompted to be created from this StackOverflow question (also written by me): VSCode Typescript Intellisense not suggesting methods from dynamic type or interface

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugA bug in TypeScriptDomain: LS: Completion ListsThe issue relates to showing completion lists in an editor

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions