Skip to content

Commit 2f798a5

Browse files
committed
Exclude indexed access type object literal completions from mixing in base constraint completions
1 parent 5fc917b commit 2f798a5

File tree

5 files changed

+99
-5
lines changed

5 files changed

+99
-5
lines changed

src/compiler/checker.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21337,11 +21337,19 @@ namespace ts {
2133721337
return argIndex === -1 ? undefined : getContextualTypeForArgumentAtIndex(callTarget, argIndex, contextFlags);
2133821338
}
2133921339

21340-
function getContextualTypeForArgumentAtIndex(callTarget: CallLikeExpression, argIndex: number, contextFlags?: ContextFlags): Type {
21340+
function getContextualTypeForArgumentAtIndex(callTarget: CallLikeExpression, argIndex: number, contextFlags?: ContextFlags): Type | undefined {
2134121341
// If we're already in the process of resolving the given signature, don't resolve again as
2134221342
// that could cause infinite recursion. Instead, return anySignature.
2134321343
let signature = getNodeLinks(callTarget).resolvedSignature === resolvingSignature ? resolvingSignature : getResolvedSignature(callTarget);
21344-
if (contextFlags && contextFlags & ContextFlags.BaseConstraint && signature.target && !hasTypeArguments(callTarget)) {
21344+
21345+
if (contextFlags && contextFlags & ContextFlags.Uninstantiated) {
21346+
return signature.target ? getTypeAtPosition(signature.target, argIndex) : undefined;
21347+
}
21348+
21349+
if (contextFlags && contextFlags & ContextFlags.BaseConstraint) {
21350+
if (!signature.target || hasTypeArguments(callTarget)) {
21351+
return undefined;
21352+
}
2134521353
signature = getBaseSignature(signature.target);
2134621354
}
2134721355

src/compiler/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3605,6 +3605,7 @@ namespace ts {
36053605
Signature = 1 << 0, // Obtaining contextual signature
36063606
NoConstraints = 1 << 1, // Don't obtain type variable constraints
36073607
BaseConstraint = 1 << 2, // Use base constraint type for completions
3608+
Uninstantiated = 1 << 3, // Attempt to get the type from an uninstantiated signature
36083609
}
36093610

36103611
// NOTE: If modifying this enum, must modify `TypeFormatFlags` too!

src/services/completions.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1800,9 +1800,14 @@ namespace ts.Completions {
18001800

18011801
if (objectLikeContainer.kind === SyntaxKind.ObjectLiteralExpression) {
18021802
const instantiatedType = typeChecker.getContextualType(objectLikeContainer);
1803-
const baseType = instantiatedType && typeChecker.getContextualType(objectLikeContainer, ContextFlags.BaseConstraint);
1804-
if (!instantiatedType || !baseType) return GlobalsSearch.Fail;
1805-
isNewIdentifierLocation = hasIndexSignature(instantiatedType || baseType);
1803+
if (!instantiatedType) return GlobalsSearch.Fail;
1804+
const uninstantiatedType = typeChecker.getContextualType(objectLikeContainer, ContextFlags.Uninstantiated);
1805+
let baseType;
1806+
if (!(uninstantiatedType && uninstantiatedType.flags & TypeFlags.IndexedAccess)) {
1807+
baseType = typeChecker.getContextualType(objectLikeContainer, ContextFlags.BaseConstraint);
1808+
}
1809+
1810+
isNewIdentifierLocation = hasIndexSignature(instantiatedType);
18061811
typeMembers = getPropertiesForObjectExpression(instantiatedType, baseType, objectLikeContainer, typeChecker);
18071812
existingMembers = objectLikeContainer.properties;
18081813
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
////interface CustomElements {
4+
//// 'component-one': {
5+
//// foo?: string;
6+
//// },
7+
//// 'component-two': {
8+
//// bar?: string;
9+
//// }
10+
////}
11+
////
12+
////interface Options<T extends keyof CustomElements> {
13+
//// props: CustomElements[T];
14+
////}
15+
////
16+
////declare function create<T extends keyof CustomElements>(name: T, options: Options<T>): void;
17+
////
18+
////create('component-one', { props: { /*1*/ } });
19+
////create('component-two', { props: { /*2*/ } });
20+
21+
verify.completions({
22+
marker: "1",
23+
exact: [{
24+
name: "foo",
25+
sortText: completion.SortText.OptionalMember
26+
}]
27+
});
28+
29+
verify.completions({
30+
marker: "2",
31+
exact: [{
32+
name: "bar",
33+
sortText: completion.SortText.OptionalMember
34+
}]
35+
});
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
////interface CustomElements {
4+
//// 'component-one': {
5+
//// foo?: string;
6+
//// },
7+
//// 'component-two': {
8+
//// bar?: string;
9+
//// }
10+
////}
11+
////
12+
////interface Options<T extends keyof CustomElements> {
13+
//// props: CustomElements[T];
14+
////}
15+
////
16+
////declare function create<T extends 'hello' | 'goodbye'>(name: T, options: Options<T extends 'hello' ? 'component-one' : 'component-two'>): void;
17+
////declare function create<T extends keyof CustomElements>(name: T, options: Options<T>): void;
18+
////
19+
////create('hello', { props: { /*1*/ } })
20+
////create('goodbye', { props: { /*2*/ } })
21+
////create('component-one', { props: { /*3*/ } });
22+
23+
verify.completions({
24+
marker: "1",
25+
exact: [{
26+
name: "foo",
27+
sortText: completion.SortText.OptionalMember
28+
}]
29+
});
30+
31+
verify.completions({
32+
marker: "2",
33+
exact: [{
34+
name: "bar",
35+
sortText: completion.SortText.OptionalMember
36+
}]
37+
});
38+
39+
verify.completions({
40+
marker: "3",
41+
exact: [{
42+
name: "foo",
43+
sortText: completion.SortText.OptionalMember
44+
}]
45+
});

0 commit comments

Comments
 (0)