diff --git a/src/services/stringCompletions.ts b/src/services/stringCompletions.ts
index 193c574732624..ec445e7aabc71 100644
--- a/src/services/stringCompletions.ts
+++ b/src/services/stringCompletions.ts
@@ -392,7 +392,7 @@ function getStringLiteralCompletionEntries(sourceFile: SourceFile, node: StringL
// });
return stringLiteralCompletionsForObjectLiteral(typeChecker, parent.parent);
}
- return fromContextualType();
+ return fromContextualType() || fromContextualType(ContextFlags.None);
case SyntaxKind.ElementAccessExpression: {
const { expression, argumentExpression } = parent as ElementAccessExpression;
@@ -432,16 +432,24 @@ function getStringLiteralCompletionEntries(sourceFile: SourceFile, node: StringL
return { kind: StringLiteralCompletionKind.Paths, paths: getStringLiteralCompletionsFromModuleNames(sourceFile, node, compilerOptions, host, typeChecker, preferences) };
case SyntaxKind.CaseClause:
const tracker = newCaseClauseTracker(typeChecker, (parent as CaseClause).parent.clauses);
- const literals = fromContextualType().types.filter(literal => !tracker.hasValue(literal.value));
+ const contextualTypes = fromContextualType();
+ if (!contextualTypes) {
+ return;
+ }
+ const literals = contextualTypes.types.filter(literal => !tracker.hasValue(literal.value));
return { kind: StringLiteralCompletionKind.Types, types: literals, isNewIdentifier: false };
default:
return fromContextualType();
}
- function fromContextualType(): StringLiteralCompletionsFromTypes {
+ function fromContextualType(contextFlags: ContextFlags = ContextFlags.Completions): StringLiteralCompletionsFromTypes | undefined {
// Get completion for string literal from string literal type
// i.e. var x: "hi" | "hello" = "/*completion position*/"
- return { kind: StringLiteralCompletionKind.Types, types: getStringLiteralTypes(getContextualTypeFromParent(node, typeChecker, ContextFlags.Completions)), isNewIdentifier: false };
+ const types = getStringLiteralTypes(getContextualTypeFromParent(node, typeChecker, contextFlags));
+ if (!types.length) {
+ return;
+ }
+ return { kind: StringLiteralCompletionKind.Types, types, isNewIdentifier: false };
}
}
diff --git a/tests/cases/fourslash/completionPreferredSuggestions1.ts b/tests/cases/fourslash/completionPreferredSuggestions1.ts
index 6b9430b4f4719..fdae8f5983bd8 100644
--- a/tests/cases/fourslash/completionPreferredSuggestions1.ts
+++ b/tests/cases/fourslash/completionPreferredSuggestions1.ts
@@ -16,5 +16,5 @@
verify.completions({ marker: "1", includes: ["a", "b", "c"] });
verify.completions({ marker: "2", includes: ["0", "1", "2"], isNewIdentifierLocation: true });
verify.completions({ marker: "3", includes: ["a", "b", "c"] });
-verify.completions({ marker: "4", excludes: ["a", "b", "c"] });
+verify.completions({ marker: "4", exact: [] });
verify.completions({ marker: "5", includes: ["a", "b", "c"] });
diff --git a/tests/cases/fourslash/completionsLiteralFromInferenceWithinInferredType1.ts b/tests/cases/fourslash/completionsLiteralFromInferenceWithinInferredType1.ts
new file mode 100644
index 0000000000000..8e2550d1a57a3
--- /dev/null
+++ b/tests/cases/fourslash/completionsLiteralFromInferenceWithinInferredType1.ts
@@ -0,0 +1,17 @@
+///
+
+// @Filename: /a.tsx
+//// declare function test(a: {
+//// [K in keyof T]: {
+//// b?: keyof T;
+//// };
+//// }): void;
+////
+//// test({
+//// foo: {},
+//// bar: {
+//// b: "/*ts*/",
+//// },
+//// });
+
+verify.completions({ marker: ["ts"], exact: ["foo", "bar"] });
diff --git a/tests/cases/fourslash/completionsLiteralFromInferenceWithinInferredType2.ts b/tests/cases/fourslash/completionsLiteralFromInferenceWithinInferredType2.ts
new file mode 100644
index 0000000000000..05647849e1de6
--- /dev/null
+++ b/tests/cases/fourslash/completionsLiteralFromInferenceWithinInferredType2.ts
@@ -0,0 +1,52 @@
+///
+
+// @Filename: /a.tsx
+//// type Values = T[keyof T];
+////
+//// type GetStates = T extends { states: object } ? T["states"] : never;
+////
+//// type IsNever = [T] extends [never] ? 1 : 0;
+////
+//// type GetIds = IsNever extends 1
+//// ? Gathered
+//// : "id" extends keyof T
+//// ? GetIds>, Gathered | `#${T["id"] & string}`>
+//// : GetIds>, Gathered>;
+////
+//// type StateConfig<
+//// TStates extends Record = Record<
+//// string,
+//// StateConfig
+//// >,
+//// TIds extends string = string
+//// > = {
+//// id?: string;
+//// initial?: keyof TStates & string;
+//// states?: {
+//// [K in keyof TStates]: StateConfig, TIds>;
+//// };
+//// on?: Record;
+//// };
+////
+//// declare function createMachine, GetIds>>(
+//// config: T
+//// ): void;
+////
+//// createMachine({
+//// initial: "child",
+//// states: {
+//// child: {
+//// initial: "foo",
+//// states: {
+//// foo: {
+//// id: "wow_deep_id",
+//// },
+//// },
+//// },
+//// },
+//// on: {
+//// EV: "/*ts*/",
+//// },
+//// });
+
+verify.completions({ marker: ["ts"], exact: ["#wow_deep_id", ".child"] });