Repository navigation
Completions for string literal type parameter don't work if it's constraint includes an empty string #47227
Description
Activity
- changed the title
[-]Completions for string literal don't work for type parameter that includes empty string in it's constraint [/-][+]Completions for string literal type parameter don't work if it's constraint includes an empty string[/+]on Dec 22, 2021 Yes that's because
""is a valid value so you don't get suggested anything once you're done writing it. But you can trigger intellisense via CTRL + SPACE while caret is between the quotes (like"|"then hit CTRL + SPACE). And thenfoo1will have suggestions andfoo2won't:Sometimes you'd want to look up all possible values before you even start typing something, which you can't do in case of
foo2Ah, yeah then it seems the issue occurres only when you have already written
""- addedExperience EnhancementNoncontroversial enhancementsNoncontroversial enhancementsSuggestionAn idea for TypeScriptAn idea for TypeScript
on Jan 5, 2022 - addedDomain: LS: Completion ListsThe issue relates to showing completion lists in an editorThe issue relates to showing completion lists in an editor
on Jan 5, 2022 Andarist commented
on Apr 21, 2022 ContributorMore actionsThis isn't really about an empty string but about the string literal at the argument position already matching one of the union members. The same happens here:
declare const foo2: <P extends "bar" | "barbaz">(p: P) => void foo2('bar') // we won't get autocomplete for 'barbaz'
Reacted by Devansh JethmalaniAndarist commented
on Apr 21, 2022 ContributorMore actionsOk, so the "problem" is that autocomplete here works on the "resolved signature" and since the function is generic this already includes an inferred generic:
declare const foo2: <P extends "bar" | "barbaz">(p: P) => void // signature: function foo2<"bar">(p: "bar"): void foo2('bar')
This can be verified~ when hovering over the function symbol at the call site.
When the generic is not properly inferred because we have a signature applicability error then the signature is displayed like this:
declare const foo2: <P extends "bar" | "barbaz">(p: P) => void // signature: const foo2: <"bar" | "barbaz">(p: "bar" | "barbaz") => void foo2('unknown')
and that is what makes it work~.
I've also looked into how this behaves with overloads and we get a full list of completions until we match one of the signatures (so basically it's very similar to single-signature functions discussed above):
declare function bar1<P extends "bar" | "barbaz">(p: P): number; declare function bar1<P extends "qwe" | "qwert">(p: P): string; // completions: "bar" | "barbaz" | "qwe" | "qwert" bar1('')
This is thanks to the fact that
getResolvedSignatureForStringLiteralCompletionsaggregates all candidate signatures in thecandidatesarray (after all nothing has been matched, so all signatures are still candidates).Some other interesting bits:
- the
resolveCall/chooseOverloadfunctions are called withinrunWithInferenceBlockedFromSourceNodebut this doesn't block signature selection - this happens with
CheckMode.IsForStringLiteralArgumentCompletionsand that potentially could be used toresolveCalldifferently here
Andrew Branch (@andrewbranch) do you see any specific implementation difficulties for this? Or a rather simple heuristic could be used here? I would have to dive deeper into how different positions within the arguments list are resolved, potentially they might need the
chooseOverloadcalls to narrow down candidates and this might be quite problematic. Ideally, we would skip inference for the particular position that is being the subject of the autocomplete while narrowing down based on other arguments etc. Since the same generic can appear at different positions this probably gets complicated quite quickly 😬- the
andrewbranch commented
on Apr 21, 2022 MemberMore actionsIsn’t this exactly what I fixed at #48410? I’m guessing not, since you’re referring to methods and CheckModes I added there. But what you’re describing is what I remember doing 😅
Andarist commented
on Apr 22, 2022 ContributorMore actionsYe, this is very related - the difference is that in the test case that you have added there is no overload selected because there is a mismatch between the arity of the function declaration and the supplied arguments count. So the added logic from your PR (#48410) handles that situation within
inferTypeArgumentsthat is called (transitively) fromgetCandidateForOverloadFailure. In this situation here - there is no overload failure though.Reacted by Andrew Branch






Bug Report
🔎 Search Terms
String literal completions, empty string constraint, intellisense
🕗 Version & Regression Information
Tested with v4.5.4
⏯ Playground Link
Playground
💻 Code
🙁 Actual behavior
foo2has no completions🙂 Expected behavior
foo2should have same completions likefoo1which are"","bar","baz"