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:
Tiago Tristao
2021-04-20 18:24:17 +01:00
committed by GitHub
parent f2705294ac
commit 3d24b85f9e
12 changed files with 241 additions and 1 deletions

View File

@@ -547,6 +547,7 @@ namespace ts {
return node && getContextualTypeForJsxAttribute(node);
},
isContextSensitive,
getTypeOfPropertyOfContextualType,
getFullyQualifiedName,
getResolvedSignature: (node, candidatesOutArray, argumentCount) =>
getResolvedSignatureWorker(node, candidatesOutArray, argumentCount, CheckMode.Normal),

View File

@@ -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.

View File

@@ -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);