Skip to content

Commit 7039e23

Browse files
committed
Slightly better I guess
1 parent 5762387 commit 7039e23

File tree

5 files changed

+57
-24
lines changed

5 files changed

+57
-24
lines changed

src/compiler/checker.ts

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21160,18 +21160,9 @@ namespace ts {
2116021160
if (isJsxOpeningLikeElement(callTarget) && argIndex === 0) {
2116121161
return getEffectiveFirstArgumentForJsxSignature(signature, callTarget);
2116221162
}
21163-
if (contextFlags && contextFlags & ContextFlags.Completion && signature.target) {
21163+
if (contextFlags && contextFlags & ContextFlags.BaseConstraint && signature.target && !tryCast(callTarget, hasTypeArguments)?.typeArguments) {
2116421164
const baseSignature = getBaseSignature(signature.target);
21165-
// Only consider the type from the base signature for completions
21166-
// if it actually contributes something. The type from the contextually
21167-
// instantiated signature is the _real_ type anyway, so we should never
21168-
// let the base type _remove_ completions from the list.
21169-
const baseArgumentType = filterType(
21170-
getTypeAtPosition(baseSignature, argIndex),
21171-
t => !(t.flags & (TypeFlags.AnyOrUnknown | TypeFlags.IndexedAccess)));
21172-
if (!(baseArgumentType.flags & TypeFlags.Never)) {
21173-
return intersectTypes(getTypeAtPosition(signature, argIndex), baseArgumentType);
21174-
}
21165+
return getTypeAtPosition(baseSignature, argIndex);
2117521166
}
2117621167
return getTypeAtPosition(signature, argIndex);
2117721168
}

src/compiler/types.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -709,6 +709,13 @@ namespace ts {
709709
| JSDocOptionalType
710710
| JSDocVariadicType;
711711

712+
export type HasTypeArguments =
713+
| CallExpression
714+
| NewExpression
715+
| TaggedTemplateExpression
716+
| JsxOpeningElement
717+
| JsxSelfClosingElement;
718+
712719
export type HasInitializer =
713720
| HasExpressionInitializer
714721
| ForStatement
@@ -3534,10 +3541,10 @@ namespace ts {
35343541

35353542
/* @internal */
35363543
export const enum ContextFlags {
3537-
None = 0,
3538-
Signature = 1 << 0, // Obtaining contextual signature
3539-
NoConstraints = 1 << 1, // Don't obtain type variable constraints
3540-
Completion = 1 << 2, // Obtaining constraint type for completion
3544+
None = 0,
3545+
Signature = 1 << 0, // Obtaining contextual signature
3546+
NoConstraints = 1 << 1, // Don't obtain type variable constraints
3547+
BaseConstraint = 1 << 2, // Use base constraint type for completions
35413548
}
35423549

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

src/compiler/utilities.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2433,6 +2433,14 @@ namespace ts {
24332433
return (node as ParameterDeclaration).dotDotDotToken !== undefined || !!type && type.kind === SyntaxKind.JSDocVariadicType;
24342434
}
24352435

2436+
export function hasTypeArguments(node: Node): node is HasTypeArguments {
2437+
return node.kind === SyntaxKind.CallExpression
2438+
|| node.kind === SyntaxKind.NewExpression
2439+
|| node.kind === SyntaxKind.TaggedTemplateExpression
2440+
|| node.kind === SyntaxKind.JsxOpeningElement
2441+
|| node.kind === SyntaxKind.JsxSelfClosingElement;
2442+
}
2443+
24362444
export const enum AssignmentKind {
24372445
None, Definite, Compound
24382446
}

src/services/completions.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1279,7 +1279,7 @@ namespace ts.Completions {
12791279
// Cursor is inside a JSX self-closing element or opening element
12801280
const attrsType = jsxContainer && typeChecker.getContextualType(jsxContainer.attributes);
12811281
if (!attrsType) return GlobalsSearch.Continue;
1282-
symbols = filterJsxAttributes(getPropertiesForObjectExpression(attrsType, jsxContainer!.attributes, typeChecker), jsxContainer!.attributes.properties);
1282+
symbols = filterJsxAttributes(getPropertiesForObjectExpression(attrsType, /*baseType*/ undefined, jsxContainer!.attributes, typeChecker), jsxContainer!.attributes.properties);
12831283
setSortTextToOptionalMember();
12841284
completionKind = CompletionKind.MemberLike;
12851285
isNewIdentifierLocation = false;
@@ -1795,10 +1795,11 @@ namespace ts.Completions {
17951795
let existingMembers: readonly Declaration[] | undefined;
17961796

17971797
if (objectLikeContainer.kind === SyntaxKind.ObjectLiteralExpression) {
1798-
const typeForObject = typeChecker.getContextualType(objectLikeContainer, ContextFlags.Completion);
1799-
if (!typeForObject) return GlobalsSearch.Fail;
1800-
isNewIdentifierLocation = hasIndexSignature(typeForObject);
1801-
typeMembers = getPropertiesForObjectExpression(typeForObject, objectLikeContainer, typeChecker);
1798+
const instantiatedType = typeChecker.getContextualType(objectLikeContainer);
1799+
const baseType = instantiatedType && typeChecker.getContextualType(objectLikeContainer, ContextFlags.BaseConstraint);
1800+
if (!instantiatedType || !baseType) return GlobalsSearch.Fail;
1801+
isNewIdentifierLocation = hasIndexSignature(instantiatedType || baseType);
1802+
typeMembers = getPropertiesForObjectExpression(instantiatedType, baseType, objectLikeContainer, typeChecker);
18021803
existingMembers = objectLikeContainer.properties;
18031804
}
18041805
else {
@@ -2535,15 +2536,19 @@ namespace ts.Completions {
25352536
return jsdoc && jsdoc.tags && (rangeContainsPosition(jsdoc, position) ? findLast(jsdoc.tags, tag => tag.pos < position) : undefined);
25362537
}
25372538

2538-
function getPropertiesForObjectExpression(contextualType: Type, obj: ObjectLiteralExpression | JsxAttributes, checker: TypeChecker): Symbol[] {
2539-
return contextualType.isUnion()
2540-
? checker.getAllPossiblePropertiesOfTypes(contextualType.types.filter(memberType =>
2539+
function getPropertiesForObjectExpression(contextualType: Type, baseConstrainedType: Type | undefined, obj: ObjectLiteralExpression | JsxAttributes, checker: TypeChecker): Symbol[] {
2540+
const type = baseConstrainedType && !(baseConstrainedType.flags & TypeFlags.AnyOrUnknown)
2541+
? checker.getUnionType([contextualType, baseConstrainedType])
2542+
: contextualType;
2543+
2544+
return type.isUnion()
2545+
? checker.getAllPossiblePropertiesOfTypes(type.types.filter(memberType =>
25412546
// If we're providing completions for an object literal, skip primitive, array-like, or callable types since those shouldn't be implemented by object literals.
25422547
!(memberType.flags & TypeFlags.Primitive ||
25432548
checker.isArrayLikeType(memberType) ||
25442549
typeHasCallOrConstructSignatures(memberType, checker) ||
25452550
checker.isTypeInvalidDueToUnionDiscriminant(memberType, obj))))
2546-
: contextualType.getApparentProperties();
2551+
: type.getApparentProperties();
25472552
}
25482553

25492554
/**
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
////declare function f<T extends string>(
4+
//// p: { a: T extends 'foo' ? { x: string } : { y: string } }
5+
////): void;
6+
////
7+
////f<'foo'>({ a: { /*1*/ } });
8+
////f<string>({ a: { /*2*/ } });
9+
10+
verify.completions({
11+
marker: '1',
12+
exact: [{
13+
name: 'x'
14+
}]
15+
});
16+
17+
verify.completions({
18+
marker: '2',
19+
exact: [{
20+
name: 'y'
21+
}]
22+
});

0 commit comments

Comments
 (0)