Skip to content

Commit 7fb821e

Browse files
author
Andy
authored
Support completions in destructuring in for-of (#16454)
1 parent 2748b3b commit 7fb821e

File tree

2 files changed

+37
-30
lines changed

2 files changed

+37
-30
lines changed

src/services/completions.ts

+25-30
Original file line numberDiff line numberDiff line change
@@ -844,7 +844,7 @@ namespace ts.Completions {
844844
*
845845
* @returns true if 'symbols' was successfully populated; false otherwise.
846846
*/
847-
function tryGetObjectLikeCompletionSymbols(objectLikeContainer: ObjectLiteralExpression | BindingPattern): boolean {
847+
function tryGetObjectLikeCompletionSymbols(objectLikeContainer: ObjectLiteralExpression | ObjectBindingPattern): boolean {
848848
// We're looking up possible property names from contextual/inferred/declared type.
849849
isMemberCompletion = true;
850850

@@ -860,41 +860,36 @@ namespace ts.Completions {
860860
typeMembers = typeChecker.getAllPossiblePropertiesOfType(typeForObject);
861861
existingMembers = (<ObjectLiteralExpression>objectLikeContainer).properties;
862862
}
863-
else if (objectLikeContainer.kind === SyntaxKind.ObjectBindingPattern) {
863+
else {
864+
Debug.assert(objectLikeContainer.kind === SyntaxKind.ObjectBindingPattern);
864865
// We are *only* completing on properties from the type being destructured.
865866
isNewIdentifierLocation = false;
866867

867868
const rootDeclaration = getRootDeclaration(objectLikeContainer.parent);
868-
if (isVariableLike(rootDeclaration)) {
869-
// We don't want to complete using the type acquired by the shape
870-
// of the binding pattern; we are only interested in types acquired
871-
// through type declaration or inference.
872-
// Also proceed if rootDeclaration is a parameter and if its containing function expression/arrow function is contextually typed -
873-
// type of parameter will flow in from the contextual type of the function
874-
let canGetType = !!(rootDeclaration.initializer || rootDeclaration.type);
875-
if (!canGetType && rootDeclaration.kind === SyntaxKind.Parameter) {
876-
if (isExpression(rootDeclaration.parent)) {
877-
canGetType = !!typeChecker.getContextualType(<Expression>rootDeclaration.parent);
878-
}
879-
else if (rootDeclaration.parent.kind === SyntaxKind.MethodDeclaration || rootDeclaration.parent.kind === SyntaxKind.SetAccessor) {
880-
canGetType = isExpression(rootDeclaration.parent.parent) && !!typeChecker.getContextualType(<Expression>rootDeclaration.parent.parent);
881-
}
869+
if (!isVariableLike(rootDeclaration)) throw Debug.fail("Root declaration is not variable-like.");
870+
871+
// We don't want to complete using the type acquired by the shape
872+
// of the binding pattern; we are only interested in types acquired
873+
// through type declaration or inference.
874+
// Also proceed if rootDeclaration is a parameter and if its containing function expression/arrow function is contextually typed -
875+
// type of parameter will flow in from the contextual type of the function
876+
let canGetType = rootDeclaration.initializer || rootDeclaration.type || rootDeclaration.parent.parent.kind === SyntaxKind.ForOfStatement;
877+
if (!canGetType && rootDeclaration.kind === SyntaxKind.Parameter) {
878+
if (isExpression(rootDeclaration.parent)) {
879+
canGetType = !!typeChecker.getContextualType(<Expression>rootDeclaration.parent);
882880
}
883-
if (canGetType) {
884-
const typeForObject = typeChecker.getTypeAtLocation(objectLikeContainer);
885-
if (!typeForObject) return false;
886-
// In a binding pattern, get only known properties. Everywhere else we will get all possible properties.
887-
typeMembers = typeChecker.getPropertiesOfType(typeForObject);
888-
existingMembers = (<ObjectBindingPattern>objectLikeContainer).elements;
881+
else if (rootDeclaration.parent.kind === SyntaxKind.MethodDeclaration || rootDeclaration.parent.kind === SyntaxKind.SetAccessor) {
882+
canGetType = isExpression(rootDeclaration.parent.parent) && !!typeChecker.getContextualType(<Expression>rootDeclaration.parent.parent);
889883
}
890884
}
891-
else {
892-
Debug.fail("Root declaration is not variable-like.");
885+
if (canGetType) {
886+
const typeForObject = typeChecker.getTypeAtLocation(objectLikeContainer);
887+
if (!typeForObject) return false;
888+
// In a binding pattern, get only known properties. Everywhere else we will get all possible properties.
889+
typeMembers = typeChecker.getPropertiesOfType(typeForObject);
890+
existingMembers = (<ObjectBindingPattern>objectLikeContainer).elements;
893891
}
894892
}
895-
else {
896-
Debug.fail("Expected object literal or binding pattern, got " + objectLikeContainer.kind);
897-
}
898893

899894
if (typeMembers && typeMembers.length > 0) {
900895
// Add filtered items to the completion list
@@ -1003,14 +998,14 @@ namespace ts.Completions {
1003998
* Returns the immediate owning object literal or binding pattern of a context token,
1004999
* on the condition that one exists and that the context implies completion should be given.
10051000
*/
1006-
function tryGetObjectLikeCompletionContainer(contextToken: Node): ObjectLiteralExpression | BindingPattern {
1001+
function tryGetObjectLikeCompletionContainer(contextToken: Node): ObjectLiteralExpression | ObjectBindingPattern {
10071002
if (contextToken) {
10081003
switch (contextToken.kind) {
10091004
case SyntaxKind.OpenBraceToken: // const x = { |
10101005
case SyntaxKind.CommaToken: // const x = { a: 0, |
10111006
const parent = contextToken.parent;
1012-
if (parent && (parent.kind === SyntaxKind.ObjectLiteralExpression || parent.kind === SyntaxKind.ObjectBindingPattern)) {
1013-
return <ObjectLiteralExpression | BindingPattern>parent;
1007+
if (isObjectLiteralExpression(parent) || isObjectBindingPattern(parent)) {
1008+
return parent;
10141009
}
10151010
break;
10161011
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
////const points = [{ x: 1, y: 2 }];
4+
////points.forEach(({ /*a*/ }) => { });
5+
////const { /*b*/ } = points[0];
6+
////for (const { /*c*/ } of points) {}
7+
8+
goTo.eachMarker(() => {
9+
verify.completionListContains("x");
10+
verify.completionListContains("y");
11+
verify.completionListCount(2);
12+
});

0 commit comments

Comments
 (0)