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"] });