Skip to content

Commit b95187d

Browse files
authored
Provide string completions for in keyword checks (#60272)
1 parent 0d01692 commit b95187d

File tree

2 files changed

+54
-1
lines changed

2 files changed

+54
-1
lines changed

src/services/stringCompletions.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
addToSeen,
1313
altDirectorySeparator,
1414
arrayFrom,
15+
BinaryExpression,
1516
CallLikeExpression,
1617
CancellationToken,
1718
CaseClause,
@@ -491,7 +492,17 @@ function getStringLiteralCompletionEntries(sourceFile: SourceFile, node: StringL
491492
const existing = new Set(namedImportsOrExports.elements.map(n => moduleExportNameTextEscaped(n.propertyName || n.name)));
492493
const uniques = exports.filter(e => e.escapedName !== InternalSymbolName.Default && !existing.has(e.escapedName));
493494
return { kind: StringLiteralCompletionKind.Properties, symbols: uniques, hasIndexSignature: false };
494-
495+
case SyntaxKind.BinaryExpression:
496+
if ((parent as BinaryExpression).operatorToken.kind === SyntaxKind.InKeyword) {
497+
const type = typeChecker.getTypeAtLocation((parent as BinaryExpression).right);
498+
const properties = type.isUnion() ? typeChecker.getAllPossiblePropertiesOfTypes(type.types) : type.getApparentProperties();
499+
return {
500+
kind: StringLiteralCompletionKind.Properties,
501+
symbols: properties.filter(prop => !prop.valueDeclaration || !isPrivateIdentifierClassElementDeclaration(prop.valueDeclaration)),
502+
hasIndexSignature: false,
503+
};
504+
}
505+
return fromContextualType(ContextFlags.None);
495506
default:
496507
return fromContextualType() || fromContextualType(ContextFlags.None);
497508
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
// @target: esnext
4+
5+
//// declare const obj: {
6+
//// a?: string;
7+
//// b: number;
8+
//// };
9+
////
10+
//// if ("/*1*/" in obj) {}
11+
//// if (((("/*2*/"))) in obj) {}
12+
//// if ("/*3*/" in (((obj)))) {}
13+
//// if (((("/*4*/"))) in (((obj)))) {}
14+
////
15+
//// type MyUnion = { missing: true } | { result: string };
16+
//// declare const u: MyUnion;
17+
//// if ("/*5*/" in u) {}
18+
////
19+
//// class Cls1 { foo = ''; #bar = 0; }
20+
//// declare const c1: Cls1;
21+
//// if ("/*6*/" in c1) {}
22+
////
23+
//// class Cls2 { foo = ''; private bar = 0; }
24+
//// declare const c2: Cls2;
25+
//// if ("/*7*/" in c2) {}
26+
27+
verify.completions({
28+
marker: ["1", "2", "3", "4"],
29+
exact: ["a", "b"],
30+
});
31+
verify.completions({
32+
marker: "5",
33+
exact: ["missing", "result"],
34+
});
35+
verify.completions({
36+
marker: "6",
37+
exact: ["foo"],
38+
});
39+
verify.completions({
40+
marker: "7",
41+
exact: ["bar", "foo"],
42+
});

0 commit comments

Comments
 (0)