Skip to content

Commit 1a76569

Browse files
authored
Retry querying string completions from the inferred type if the original completions request doesn't return anything (microsoft#52875)
1 parent f2df10f commit 1a76569

4 files changed

+82
-5
lines changed

src/services/stringCompletions.ts

+12-4
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,7 @@ function getStringLiteralCompletionEntries(sourceFile: SourceFile, node: StringL
392392
// });
393393
return stringLiteralCompletionsForObjectLiteral(typeChecker, parent.parent);
394394
}
395-
return fromContextualType();
395+
return fromContextualType() || fromContextualType(ContextFlags.None);
396396

397397
case SyntaxKind.ElementAccessExpression: {
398398
const { expression, argumentExpression } = parent as ElementAccessExpression;
@@ -432,16 +432,24 @@ function getStringLiteralCompletionEntries(sourceFile: SourceFile, node: StringL
432432
return { kind: StringLiteralCompletionKind.Paths, paths: getStringLiteralCompletionsFromModuleNames(sourceFile, node, compilerOptions, host, typeChecker, preferences) };
433433
case SyntaxKind.CaseClause:
434434
const tracker = newCaseClauseTracker(typeChecker, (parent as CaseClause).parent.clauses);
435-
const literals = fromContextualType().types.filter(literal => !tracker.hasValue(literal.value));
435+
const contextualTypes = fromContextualType();
436+
if (!contextualTypes) {
437+
return;
438+
}
439+
const literals = contextualTypes.types.filter(literal => !tracker.hasValue(literal.value));
436440
return { kind: StringLiteralCompletionKind.Types, types: literals, isNewIdentifier: false };
437441
default:
438442
return fromContextualType();
439443
}
440444

441-
function fromContextualType(): StringLiteralCompletionsFromTypes {
445+
function fromContextualType(contextFlags: ContextFlags = ContextFlags.Completions): StringLiteralCompletionsFromTypes | undefined {
442446
// Get completion for string literal from string literal type
443447
// i.e. var x: "hi" | "hello" = "/*completion position*/"
444-
return { kind: StringLiteralCompletionKind.Types, types: getStringLiteralTypes(getContextualTypeFromParent(node, typeChecker, ContextFlags.Completions)), isNewIdentifier: false };
448+
const types = getStringLiteralTypes(getContextualTypeFromParent(node, typeChecker, contextFlags));
449+
if (!types.length) {
450+
return;
451+
}
452+
return { kind: StringLiteralCompletionKind.Types, types, isNewIdentifier: false };
445453
}
446454
}
447455

tests/cases/fourslash/completionPreferredSuggestions1.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,5 @@
1616
verify.completions({ marker: "1", includes: ["a", "b", "c"] });
1717
verify.completions({ marker: "2", includes: ["0", "1", "2"], isNewIdentifierLocation: true });
1818
verify.completions({ marker: "3", includes: ["a", "b", "c"] });
19-
verify.completions({ marker: "4", excludes: ["a", "b", "c"] });
19+
verify.completions({ marker: "4", exact: [] });
2020
verify.completions({ marker: "5", includes: ["a", "b", "c"] });
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
// @Filename: /a.tsx
4+
//// declare function test<T>(a: {
5+
//// [K in keyof T]: {
6+
//// b?: keyof T;
7+
//// };
8+
//// }): void;
9+
////
10+
//// test({
11+
//// foo: {},
12+
//// bar: {
13+
//// b: "/*ts*/",
14+
//// },
15+
//// });
16+
17+
verify.completions({ marker: ["ts"], exact: ["foo", "bar"] });
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
// @Filename: /a.tsx
4+
//// type Values<T> = T[keyof T];
5+
////
6+
//// type GetStates<T> = T extends { states: object } ? T["states"] : never;
7+
////
8+
//// type IsNever<T> = [T] extends [never] ? 1 : 0;
9+
////
10+
//// type GetIds<T, Gathered extends string = never> = IsNever<T> extends 1
11+
//// ? Gathered
12+
//// : "id" extends keyof T
13+
//// ? GetIds<Values<GetStates<T>>, Gathered | `#${T["id"] & string}`>
14+
//// : GetIds<Values<GetStates<T>>, Gathered>;
15+
////
16+
//// type StateConfig<
17+
//// TStates extends Record<string, StateConfig> = Record<
18+
//// string,
19+
//// StateConfig<any>
20+
//// >,
21+
//// TIds extends string = string
22+
//// > = {
23+
//// id?: string;
24+
//// initial?: keyof TStates & string;
25+
//// states?: {
26+
//// [K in keyof TStates]: StateConfig<GetStates<TStates[K]>, TIds>;
27+
//// };
28+
//// on?: Record<string, TIds | `.${keyof TStates & string}`>;
29+
//// };
30+
////
31+
//// declare function createMachine<const T extends StateConfig<GetStates<T>, GetIds<T>>>(
32+
//// config: T
33+
//// ): void;
34+
////
35+
//// createMachine({
36+
//// initial: "child",
37+
//// states: {
38+
//// child: {
39+
//// initial: "foo",
40+
//// states: {
41+
//// foo: {
42+
//// id: "wow_deep_id",
43+
//// },
44+
//// },
45+
//// },
46+
//// },
47+
//// on: {
48+
//// EV: "/*ts*/",
49+
//// },
50+
//// });
51+
52+
verify.completions({ marker: ["ts"], exact: ["#wow_deep_id", ".child"] });

0 commit comments

Comments
 (0)