mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-15 12:51:30 -05:00
fix(26141): show completions for string parenthesized types (#39697)
This commit is contained in:
@@ -108,12 +108,19 @@ namespace ts.Completions.StringCompletions {
|
||||
}
|
||||
type StringLiteralCompletion = { readonly kind: StringLiteralCompletionKind.Paths, readonly paths: readonly PathCompletion[] } | StringLiteralCompletionsFromProperties | StringLiteralCompletionsFromTypes;
|
||||
function getStringLiteralCompletionEntries(sourceFile: SourceFile, node: StringLiteralLike, position: number, typeChecker: TypeChecker, compilerOptions: CompilerOptions, host: LanguageServiceHost): StringLiteralCompletion | undefined {
|
||||
const { parent } = node;
|
||||
const parent = walkUpParentheses(node.parent);
|
||||
switch (parent.kind) {
|
||||
case SyntaxKind.LiteralType:
|
||||
switch (parent.parent.kind) {
|
||||
case SyntaxKind.TypeReference:
|
||||
return { kind: StringLiteralCompletionKind.Types, types: getStringLiteralTypes(typeChecker.getTypeArgumentConstraint(parent as LiteralTypeNode)), isNewIdentifier: false };
|
||||
case SyntaxKind.LiteralType: {
|
||||
const grandParent = walkUpParentheses(parent.parent);
|
||||
switch (grandParent.kind) {
|
||||
case SyntaxKind.TypeReference: {
|
||||
const typeReference = grandParent as TypeReferenceNode;
|
||||
const typeArgument = findAncestor(parent, n => n.parent === typeReference) as LiteralTypeNode;
|
||||
if (typeArgument) {
|
||||
return { kind: StringLiteralCompletionKind.Types, types: getStringLiteralTypes(typeChecker.getTypeArgumentConstraint(typeArgument)), isNewIdentifier: false };
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
case SyntaxKind.IndexedAccessType:
|
||||
// Get all apparent property names
|
||||
// i.e. interface Foo {
|
||||
@@ -121,19 +128,21 @@ namespace ts.Completions.StringCompletions {
|
||||
// bar: string;
|
||||
// }
|
||||
// let x: Foo["/*completion position*/"]
|
||||
return stringLiteralCompletionsFromProperties(typeChecker.getTypeFromTypeNode((parent.parent as IndexedAccessTypeNode).objectType));
|
||||
return stringLiteralCompletionsFromProperties(typeChecker.getTypeFromTypeNode((grandParent as IndexedAccessTypeNode).objectType));
|
||||
case SyntaxKind.ImportType:
|
||||
return { kind: StringLiteralCompletionKind.Paths, paths: getStringLiteralCompletionsFromModuleNames(sourceFile, node, compilerOptions, host, typeChecker) };
|
||||
case SyntaxKind.UnionType: {
|
||||
if (!isTypeReferenceNode(parent.parent.parent)) return undefined;
|
||||
const alreadyUsedTypes = getAlreadyUsedTypesInStringLiteralUnion(parent.parent as UnionTypeNode, parent as LiteralTypeNode);
|
||||
const types = getStringLiteralTypes(typeChecker.getTypeArgumentConstraint(parent.parent as UnionTypeNode)).filter(t => !contains(alreadyUsedTypes, t.value));
|
||||
if (!isTypeReferenceNode(grandParent.parent)) {
|
||||
return undefined;
|
||||
}
|
||||
const alreadyUsedTypes = getAlreadyUsedTypesInStringLiteralUnion(grandParent as UnionTypeNode, parent as LiteralTypeNode);
|
||||
const types = getStringLiteralTypes(typeChecker.getTypeArgumentConstraint(grandParent as UnionTypeNode)).filter(t => !contains(alreadyUsedTypes, t.value));
|
||||
return { kind: StringLiteralCompletionKind.Types, types, isNewIdentifier: false };
|
||||
}
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
|
||||
}
|
||||
case SyntaxKind.PropertyAssignment:
|
||||
if (isObjectLiteralExpression(parent.parent) && (<PropertyAssignment>parent).name === node) {
|
||||
// Get quoted name of properties of the object literal expression
|
||||
@@ -154,7 +163,7 @@ namespace ts.Completions.StringCompletions {
|
||||
|
||||
case SyntaxKind.ElementAccessExpression: {
|
||||
const { expression, argumentExpression } = parent as ElementAccessExpression;
|
||||
if (node === argumentExpression) {
|
||||
if (node === skipParentheses(argumentExpression)) {
|
||||
// Get all names of properties on the expression
|
||||
// i.e. interface A {
|
||||
// 'prop1': string
|
||||
@@ -199,6 +208,17 @@ namespace ts.Completions.StringCompletions {
|
||||
}
|
||||
}
|
||||
|
||||
function walkUpParentheses(node: Node) {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.ParenthesizedType:
|
||||
return walkUpParenthesizedTypes(node);
|
||||
case SyntaxKind.ParenthesizedExpression:
|
||||
return walkUpParenthesizedExpressions(node);
|
||||
default:
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
function getAlreadyUsedTypesInStringLiteralUnion(union: UnionTypeNode, current: LiteralTypeNode): readonly string[] {
|
||||
return mapDefined(union.types, type =>
|
||||
type !== current && isLiteralTypeNode(type) && isStringLiteral(type.literal) ? type.literal.text : undefined);
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
////const foo = {
|
||||
//// a: 1,
|
||||
//// b: 1,
|
||||
//// c: 1
|
||||
////}
|
||||
////const a = foo["[|/*1*/|]"];
|
||||
////const b = foo[("[|/*2*/|]")];
|
||||
////const c = foo[(("[|/*3*/|]"))];
|
||||
|
||||
const [r1, r2, r3] = test.ranges();
|
||||
verify.completions(
|
||||
{
|
||||
marker: "1",
|
||||
exact: [
|
||||
{ name: "a", replacementSpan: r1 },
|
||||
{ name: "b", replacementSpan: r1 },
|
||||
{ name: "c", replacementSpan: r1 }
|
||||
]
|
||||
},
|
||||
{
|
||||
marker: "2",
|
||||
exact: [
|
||||
{ name: "a", replacementSpan: r2 },
|
||||
{ name: "b", replacementSpan: r2 },
|
||||
{ name: "c", replacementSpan: r2 }
|
||||
]
|
||||
},
|
||||
{
|
||||
marker: "3",
|
||||
exact: [
|
||||
{ name: "a", replacementSpan: r3 },
|
||||
{ name: "b", replacementSpan: r3 },
|
||||
{ name: "c", replacementSpan: r3 }
|
||||
]
|
||||
}
|
||||
);
|
||||
@@ -0,0 +1,85 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
////type T1 = "a" | "b" | "c";
|
||||
////type T2<T extends T1> = {};
|
||||
////
|
||||
////type T3 = T2<"[|/*1*/|]">;
|
||||
////type T4 = T2<("[|/*2*/|]")>;
|
||||
////type T5 = T2<(("[|/*3*/|]"))>;
|
||||
////type T6 = T2<((("[|/*4*/|]")))>;
|
||||
////
|
||||
////type T7<P extends T1, K extends T1> = {};
|
||||
////type T8 = T7<"a", ((("[|/*5*/|]")))>;
|
||||
////
|
||||
////interface Foo {
|
||||
//// a: number;
|
||||
//// b: number;
|
||||
////}
|
||||
////const a: Foo["[|/*6*/|]"];
|
||||
////const b: Foo[("[|/*7*/|]")];
|
||||
////const b: Foo[(("[|/*8*/|]"))];
|
||||
|
||||
const [r1, r2, r3, r4, r5, r6, r7, r8] = test.ranges();
|
||||
verify.completions(
|
||||
{
|
||||
marker: "1",
|
||||
exact: [
|
||||
{ name: "a", replacementSpan: r1 },
|
||||
{ name: "b", replacementSpan: r1 },
|
||||
{ name: "c", replacementSpan: r1 }
|
||||
]
|
||||
},
|
||||
{
|
||||
marker: "2",
|
||||
exact: [
|
||||
{ name: "a", replacementSpan: r2 },
|
||||
{ name: "b", replacementSpan: r2 },
|
||||
{ name: "c", replacementSpan: r2 }
|
||||
]
|
||||
},
|
||||
{
|
||||
marker: "3",
|
||||
exact: [
|
||||
{ name: "a", replacementSpan: r3 },
|
||||
{ name: "b", replacementSpan: r3 },
|
||||
{ name: "c", replacementSpan: r3 }
|
||||
]
|
||||
},
|
||||
{
|
||||
marker: "4",
|
||||
exact: [
|
||||
{ name: "a", replacementSpan: r4 },
|
||||
{ name: "b", replacementSpan: r4 },
|
||||
{ name: "c", replacementSpan: r4 }
|
||||
]
|
||||
},
|
||||
{
|
||||
marker: "5",
|
||||
exact: [
|
||||
{ name: "a", replacementSpan: r5 },
|
||||
{ name: "b", replacementSpan: r5 },
|
||||
{ name: "c", replacementSpan: r5 }
|
||||
]
|
||||
},
|
||||
{
|
||||
marker: "6",
|
||||
exact: [
|
||||
{ name: "a", replacementSpan: r6 },
|
||||
{ name: "b", replacementSpan: r6 }
|
||||
]
|
||||
},
|
||||
{
|
||||
marker: "7",
|
||||
exact: [
|
||||
{ name: "a", replacementSpan: r7 },
|
||||
{ name: "b", replacementSpan: r7 }
|
||||
]
|
||||
},
|
||||
{
|
||||
marker: "8",
|
||||
exact: [
|
||||
{ name: "a", replacementSpan: r8 },
|
||||
{ name: "b", replacementSpan: r8 }
|
||||
]
|
||||
}
|
||||
);
|
||||
Reference in New Issue
Block a user