mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-04 21:53:42 -06:00
Completion list for type literals in type arguments (#43526)
* Completion list for type literals in type arguments * Add tests * Refactor for better readability * - Support non-identifier keys - Move main logic onto tryGetGlobalSymbols function
This commit is contained in:
parent
f2705294ac
commit
3d24b85f9e
@ -547,6 +547,7 @@ namespace ts {
|
||||
return node && getContextualTypeForJsxAttribute(node);
|
||||
},
|
||||
isContextSensitive,
|
||||
getTypeOfPropertyOfContextualType,
|
||||
getFullyQualifiedName,
|
||||
getResolvedSignature: (node, candidatesOutArray, argumentCount) =>
|
||||
getResolvedSignatureWorker(node, candidatesOutArray, argumentCount, CheckMode.Normal),
|
||||
|
||||
@ -4144,6 +4144,7 @@ namespace ts {
|
||||
/* @internal */ getContextualTypeForArgumentAtIndex(call: CallLikeExpression, argIndex: number): Type | undefined;
|
||||
/* @internal */ getContextualTypeForJsxAttribute(attribute: JsxAttribute | JsxSpreadAttribute): Type | undefined;
|
||||
/* @internal */ isContextSensitive(node: Expression | MethodDeclaration | ObjectLiteralElementLike | JsxAttributeLike): boolean;
|
||||
/* @internal */ getTypeOfPropertyOfContextualType(type: Type, name: __String): Type | undefined;
|
||||
|
||||
/**
|
||||
* returns unknownSignature in the case of an error.
|
||||
|
||||
@ -1510,7 +1510,8 @@ namespace ts.Completions {
|
||||
}
|
||||
|
||||
function tryGetGlobalSymbols(): boolean {
|
||||
const result: GlobalsSearch = tryGetObjectLikeCompletionSymbols()
|
||||
const result: GlobalsSearch = tryGetObjectTypeLiteralInTypeArgumentCompletionSymbols()
|
||||
|| tryGetObjectLikeCompletionSymbols()
|
||||
|| tryGetImportCompletionSymbols()
|
||||
|| tryGetImportOrExportClauseCompletionSymbols()
|
||||
|| tryGetLocalNamedExportCompletionSymbols()
|
||||
@ -1913,6 +1914,32 @@ namespace ts.Completions {
|
||||
position === contextToken.end && (!!contextToken.isUnterminated || isRegularExpressionLiteral(contextToken)));
|
||||
}
|
||||
|
||||
function tryGetObjectTypeLiteralInTypeArgumentCompletionSymbols(): GlobalsSearch | undefined {
|
||||
const typeLiteralNode = tryGetTypeLiteralNode(contextToken);
|
||||
if (!typeLiteralNode) return GlobalsSearch.Continue;
|
||||
|
||||
const intersectionTypeNode = isIntersectionTypeNode(typeLiteralNode.parent) ? typeLiteralNode.parent : undefined;
|
||||
const containerTypeNode = intersectionTypeNode || typeLiteralNode;
|
||||
|
||||
const containerExpectedType = getConstraintOfTypeArgumentProperty(containerTypeNode, typeChecker);
|
||||
if (!containerExpectedType) return GlobalsSearch.Continue;
|
||||
|
||||
const containerActualType = typeChecker.getTypeFromTypeNode(containerTypeNode);
|
||||
|
||||
const members = getPropertiesForCompletion(containerExpectedType, typeChecker);
|
||||
const existingMembers = getPropertiesForCompletion(containerActualType, typeChecker);
|
||||
|
||||
const existingMemberEscapedNames: Set<__String> = new Set();
|
||||
existingMembers.forEach(s => existingMemberEscapedNames.add(s.escapedName));
|
||||
|
||||
symbols = filter(members, s => !existingMemberEscapedNames.has(s.escapedName));
|
||||
|
||||
completionKind = CompletionKind.ObjectPropertyDeclaration;
|
||||
isNewIdentifierLocation = true;
|
||||
|
||||
return GlobalsSearch.Success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Aggregates relevant symbols for completion in object literals and object binding patterns.
|
||||
* Relevant symbols are stored in the captured 'symbols' variable.
|
||||
@ -2859,6 +2886,49 @@ namespace ts.Completions {
|
||||
}
|
||||
}
|
||||
|
||||
function tryGetTypeLiteralNode(node: Node): TypeLiteralNode | undefined {
|
||||
if (!node) return undefined;
|
||||
|
||||
const parent = node.parent;
|
||||
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.OpenBraceToken:
|
||||
if (isTypeLiteralNode(parent)) {
|
||||
return parent;
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.SemicolonToken:
|
||||
case SyntaxKind.CommaToken:
|
||||
case SyntaxKind.Identifier:
|
||||
if (parent.kind === SyntaxKind.PropertySignature && isTypeLiteralNode(parent.parent)) {
|
||||
return parent.parent;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getConstraintOfTypeArgumentProperty(node: Node, checker: TypeChecker): Type | undefined {
|
||||
if (!node) return undefined;
|
||||
|
||||
if (isTypeNode(node) && isTypeReferenceType(node.parent)) {
|
||||
return checker.getTypeArgumentConstraint(node);
|
||||
}
|
||||
|
||||
const t = getConstraintOfTypeArgumentProperty(node.parent, checker);
|
||||
if (!t) return undefined;
|
||||
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.PropertySignature:
|
||||
return checker.getTypeOfPropertyOfContextualType(t, node.symbol.escapedName);
|
||||
case SyntaxKind.IntersectionType:
|
||||
case SyntaxKind.TypeLiteral:
|
||||
case SyntaxKind.UnionType:
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: GH#19856 Would like to return `node is Node & { parent: (ClassElement | TypeElement) & { parent: ObjectTypeDeclaration } }` but then compilation takes > 10 minutes
|
||||
function isFromObjectTypeDeclaration(node: Node): boolean {
|
||||
return node.parent && isClassOrTypeElement(node.parent) && isObjectTypeDeclaration(node.parent.parent);
|
||||
|
||||
@ -0,0 +1,23 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
////interface Foo {
|
||||
//// one: string;
|
||||
//// two: number;
|
||||
//// 333: symbol;
|
||||
//// '4four': boolean;
|
||||
//// '5 five': object;
|
||||
//// number: string;
|
||||
//// Object: number;
|
||||
////}
|
||||
////
|
||||
////interface Bar<T extends Foo> {
|
||||
//// foo: T;
|
||||
////}
|
||||
////
|
||||
////var foobar: Bar<{/**/
|
||||
|
||||
verify.completions({
|
||||
marker: "",
|
||||
exact: ["one", "two", "\"333\"", "\"4four\"", "\"5 five\"", "number", "Object"],
|
||||
isNewIdentifierLocation: true
|
||||
});
|
||||
@ -0,0 +1,14 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
////interface Foo {
|
||||
//// one: string;
|
||||
//// two: number;
|
||||
////}
|
||||
////
|
||||
////interface Bar<T extends Foo> {
|
||||
//// foo: T;
|
||||
////}
|
||||
////
|
||||
////var foobar: Bar<{ on/**/
|
||||
|
||||
verify.completions({ marker: "", exact: ["one", "two"], isNewIdentifierLocation: true });
|
||||
@ -0,0 +1,14 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
////interface Foo {
|
||||
//// one: string;
|
||||
//// two: number;
|
||||
////}
|
||||
////
|
||||
////interface Bar<T extends Foo> {
|
||||
//// foo: T;
|
||||
////}
|
||||
////
|
||||
////var foobar: Bar<{ one: string, /**/
|
||||
|
||||
verify.completions({ marker: "", exact: "two", isNewIdentifierLocation: true });
|
||||
@ -0,0 +1,14 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
////interface Foo {
|
||||
//// one: string;
|
||||
//// two: number;
|
||||
////}
|
||||
////
|
||||
////interface Bar<T extends Foo> {
|
||||
//// foo: T;
|
||||
////}
|
||||
////
|
||||
////var foobar: Bar<{ one: string } & {/**/
|
||||
|
||||
verify.completions({ marker: "", exact: "two", isNewIdentifierLocation: true });
|
||||
@ -0,0 +1,14 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
////interface Foo {
|
||||
//// one: string;
|
||||
//// two: number;
|
||||
////}
|
||||
////
|
||||
////interface Bar<T extends Foo> {
|
||||
//// foo: T;
|
||||
////}
|
||||
////
|
||||
////var foobar: Bar<{ prop1: string } & {/**/
|
||||
|
||||
verify.completions({ marker: "", exact: ["one", "two"], isNewIdentifierLocation: true });
|
||||
@ -0,0 +1,14 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
////interface Foo {
|
||||
//// one: string;
|
||||
//// two: number;
|
||||
////}
|
||||
////
|
||||
////interface Bar<T extends Foo> {
|
||||
//// foo: T;
|
||||
////}
|
||||
////
|
||||
////var foobar: Bar<{ one: string } | {/**/
|
||||
|
||||
verify.completions({ marker: "", exact: ["one", "two"], isNewIdentifierLocation: true });
|
||||
@ -0,0 +1,17 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
////interface Foo {
|
||||
//// one: string;
|
||||
//// two: {
|
||||
//// three: number;
|
||||
//// }
|
||||
////}
|
||||
////
|
||||
////interface Bar<T extends Foo> {
|
||||
//// foo: T;
|
||||
////}
|
||||
////
|
||||
////var foobar: Bar<{
|
||||
//// two: {/**/
|
||||
|
||||
verify.completions({ marker: "", exact: "three", isNewIdentifierLocation: true });
|
||||
@ -0,0 +1,33 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
////interface Foo {
|
||||
//// one: string;
|
||||
//// two: {
|
||||
//// three: {
|
||||
//// four: number;
|
||||
//// five: string;
|
||||
//// }
|
||||
//// }
|
||||
////}
|
||||
////
|
||||
////interface Bar<T extends Foo> {
|
||||
//// foo: T;
|
||||
////}
|
||||
////
|
||||
////var foobar: Bar<{
|
||||
//// two: {
|
||||
//// three: {
|
||||
//// five: string,
|
||||
//// /*4*/
|
||||
//// },
|
||||
//// /*0*/
|
||||
//// },
|
||||
//// /*1*/
|
||||
////}>;
|
||||
|
||||
verify.completions(
|
||||
{ marker: "4", exact: "four", isNewIdentifierLocation: true },
|
||||
{ marker: "0", exact: [], isNewIdentifierLocation: true },
|
||||
{ marker: "1", exact: "one", isNewIdentifierLocation: true },
|
||||
);
|
||||
|
||||
@ -0,0 +1,25 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
////interface Foo {
|
||||
//// one: string;
|
||||
//// two: {
|
||||
//// three: {
|
||||
//// four: number;
|
||||
//// five: string;
|
||||
//// }
|
||||
//// }
|
||||
////}
|
||||
////
|
||||
////interface Bar<T extends Foo> {
|
||||
//// foo: T;
|
||||
////}
|
||||
////
|
||||
////var foobar: Bar<{
|
||||
//// two: {
|
||||
//// three: { five:/*g*/ } & {/*4*/},
|
||||
//// }
|
||||
////}>;
|
||||
|
||||
verify.completions({ marker: "g", includes: ["Foo", "Bar", ...completion.globalTypes] });
|
||||
verify.completions({ marker: "4", exact: "four", isNewIdentifierLocation: true });
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user