Skip to content

Commit 4aaccf6

Browse files
committed
fixes #30507
1 parent a150d55 commit 4aaccf6

File tree

7 files changed

+70
-32
lines changed

7 files changed

+70
-32
lines changed

src/compiler/checker.ts

Lines changed: 24 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -216,9 +216,9 @@ namespace ts {
216216
},
217217
getAugmentedPropertiesOfType,
218218
getRootSymbols,
219-
getContextualType: nodeIn => {
219+
getContextualType: (nodeIn, contextFlags) => {
220220
const node = getParseTreeNode(nodeIn, isExpression);
221-
return node ? getContextualType(node) : undefined;
221+
return node ? getContextualType(node, contextFlags) : undefined;
222222
},
223223
getContextualTypeForObjectLiteralElement: nodeIn => {
224224
const node = getParseTreeNode(nodeIn, isObjectLiteralElementLike);
@@ -712,11 +712,6 @@ namespace ts {
712712
IsForSignatureHelp = 1 << 4, // Call resolution for purposes of signature help
713713
}
714714

715-
const enum ContextFlags {
716-
None = 0,
717-
Signature = 1 << 0, // Obtaining contextual signature
718-
}
719-
720715
const enum AccessFlags {
721716
None = 0,
722717
NoIndexSignatures = 1 << 0,
@@ -15689,7 +15684,7 @@ namespace ts {
1568915684
return getWidenedType(unwidenedType);
1569015685
}
1569115686

15692-
function getInferredType(context: InferenceContext, index: number): Type {
15687+
function getInferredType(context: InferenceContext, index: number, contextFlags?: ContextFlags): Type {
1569315688
const inference = context.inferences[index];
1569415689
if (!inference.inferredType) {
1569515690
let inferredType: Type | undefined;
@@ -15734,7 +15729,8 @@ namespace ts {
1573415729
const constraint = getConstraintOfTypeParameter(inference.typeParameter);
1573515730
if (constraint) {
1573615731
const instantiatedConstraint = instantiateType(constraint, context.nonFixingMapper);
15737-
if (!inferredType || !context.compareTypes(inferredType, getTypeWithThisArgument(instantiatedConstraint, inferredType))) {
15732+
const isCompletionContext = contextFlags && ((contextFlags | ContextFlags.Completion) === contextFlags);
15733+
if (!inferredType || !context.compareTypes(inferredType, getTypeWithThisArgument(instantiatedConstraint, inferredType)) || isCompletionContext) {
1573815734
inference.inferredType = inferredType = instantiatedConstraint;
1573915735
}
1574015736
}
@@ -15747,10 +15743,10 @@ namespace ts {
1574715743
return isInJavaScriptFile ? anyType : unknownType;
1574815744
}
1574915745

15750-
function getInferredTypes(context: InferenceContext): Type[] {
15746+
function getInferredTypes(context: InferenceContext, contextFlags?: ContextFlags): Type[] {
1575115747
const result: Type[] = [];
1575215748
for (let i = 0; i < context.inferences.length; i++) {
15753-
result.push(getInferredType(context, i));
15749+
result.push(getInferredType(context, i, contextFlags));
1575415750
}
1575515751
return result;
1575615752
}
@@ -18448,16 +18444,16 @@ namespace ts {
1844818444
}
1844918445

1845018446
// In a typed function call, an argument or substitution expression is contextually typed by the type of the corresponding parameter.
18451-
function getContextualTypeForArgument(callTarget: CallLikeExpression, arg: Expression): Type | undefined {
18447+
function getContextualTypeForArgument(callTarget: CallLikeExpression, arg: Expression, contextFlags?: ContextFlags): Type | undefined {
1845218448
const args = getEffectiveCallArguments(callTarget);
1845318449
const argIndex = args.indexOf(arg); // -1 for e.g. the expression of a CallExpression, or the tag of a TaggedTemplateExpression
18454-
return argIndex === -1 ? undefined : getContextualTypeForArgumentAtIndex(callTarget, argIndex);
18450+
return argIndex === -1 ? undefined : getContextualTypeForArgumentAtIndex(callTarget, argIndex, contextFlags);
1845518451
}
1845618452

18457-
function getContextualTypeForArgumentAtIndex(callTarget: CallLikeExpression, argIndex: number): Type {
18453+
function getContextualTypeForArgumentAtIndex(callTarget: CallLikeExpression, argIndex: number, contextFlags?: ContextFlags): Type {
1845818454
// If we're already in the process of resolving the given signature, don't resolve again as
1845918455
// that could cause infinite recursion. Instead, return anySignature.
18460-
const signature = getNodeLinks(callTarget).resolvedSignature === resolvingSignature ? resolvingSignature : getResolvedSignature(callTarget);
18456+
const signature = getNodeLinks(callTarget).resolvedSignature === resolvingSignature ? resolvingSignature : getResolvedSignature(callTarget, /*candidatesOutArray*/ undefined, /*checkMode*/ undefined, contextFlags);
1846118457
if (isJsxOpeningLikeElement(callTarget) && argIndex === 0) {
1846218458
return getEffectiveFirstArgumentForJsxSignature(signature, callTarget);
1846318459
}
@@ -18840,7 +18836,7 @@ namespace ts {
1884018836
return getContextualTypeForAwaitOperand(<AwaitExpression>parent);
1884118837
case SyntaxKind.CallExpression:
1884218838
case SyntaxKind.NewExpression:
18843-
return getContextualTypeForArgument(<CallExpression | NewExpression>parent, node);
18839+
return getContextualTypeForArgument(<CallExpression | NewExpression>parent, node, contextFlags);
1884418840
case SyntaxKind.TypeAssertionExpression:
1884518841
case SyntaxKind.AsExpression:
1884618842
return isConstTypeReference((<AssertionExpression>parent).type) ? undefined : getTypeFromTypeNode((<AssertionExpression>parent).type);
@@ -20963,7 +20959,7 @@ namespace ts {
2096320959
return getInferredTypes(context);
2096420960
}
2096520961

20966-
function inferTypeArguments(node: CallLikeExpression, signature: Signature, args: ReadonlyArray<Expression>, checkMode: CheckMode, context: InferenceContext): Type[] {
20962+
function inferTypeArguments(node: CallLikeExpression, signature: Signature, args: ReadonlyArray<Expression>, checkMode: CheckMode, context: InferenceContext, contextFlags?: ContextFlags): Type[] {
2096720963
if (isJsxOpeningLikeElement(node)) {
2096820964
return inferJsxTypeArguments(node, signature, checkMode, context);
2096920965
}
@@ -21029,7 +21025,7 @@ namespace ts {
2102921025
inferTypes(context.inferences, spreadType, restType);
2103021026
}
2103121027

21032-
return getInferredTypes(context);
21028+
return getInferredTypes(context, contextFlags);
2103321029
}
2103421030

2103521031
function getArrayifiedType(type: Type) {
@@ -21395,7 +21391,7 @@ namespace ts {
2139521391
return createDiagnosticForNodeArray(getSourceFileOfNode(node), typeArguments, Diagnostics.Expected_0_type_arguments_but_got_1, belowArgCount === -Infinity ? aboveArgCount : belowArgCount, argCount);
2139621392
}
2139721393

21398-
function resolveCall(node: CallLikeExpression, signatures: ReadonlyArray<Signature>, candidatesOutArray: Signature[] | undefined, checkMode: CheckMode, fallbackError?: DiagnosticMessage): Signature {
21394+
function resolveCall(node: CallLikeExpression, signatures: ReadonlyArray<Signature>, candidatesOutArray: Signature[] | undefined, checkMode: CheckMode, fallbackError?: DiagnosticMessage, contextFlags?: ContextFlags): Signature {
2139921395
const isTaggedTemplate = node.kind === SyntaxKind.TaggedTemplateExpression;
2140021396
const isDecorator = node.kind === SyntaxKind.Decorator;
2140121397
const isJsxOpeningOrSelfClosingElement = isJsxOpeningLikeElement(node);
@@ -21485,7 +21481,7 @@ namespace ts {
2148521481
result = chooseOverload(candidates, subtypeRelation, signatureHelpTrailingComma);
2148621482
}
2148721483
if (!result) {
21488-
result = chooseOverload(candidates, assignableRelation, signatureHelpTrailingComma);
21484+
result = chooseOverload(candidates, assignableRelation, signatureHelpTrailingComma, contextFlags);
2148921485
}
2149021486
if (result) {
2149121487
return result;
@@ -21535,7 +21531,7 @@ namespace ts {
2153521531
return node;
2153621532
}
2153721533

21538-
function chooseOverload(candidates: Signature[], relation: Map<RelationComparisonResult>, signatureHelpTrailingComma = false) {
21534+
function chooseOverload(candidates: Signature[], relation: Map<RelationComparisonResult>, signatureHelpTrailingComma = false, contextFlags?: ContextFlags) {
2153921535
candidateForArgumentError = undefined;
2154021536
candidateForArgumentArityError = undefined;
2154121537
candidateForTypeArgumentError = undefined;
@@ -21572,7 +21568,7 @@ namespace ts {
2157221568
}
2157321569
else {
2157421570
inferenceContext = createInferenceContext(candidate.typeParameters, candidate, /*flags*/ isInJSFile(node) ? InferenceFlags.AnyDefault : InferenceFlags.None);
21575-
typeArgumentTypes = inferTypeArguments(node, candidate, args, argCheckMode | CheckMode.SkipGenericFunctions, inferenceContext);
21571+
typeArgumentTypes = inferTypeArguments(node, candidate, args, argCheckMode | CheckMode.SkipGenericFunctions, inferenceContext, contextFlags);
2157621572
argCheckMode |= inferenceContext.flags & InferenceFlags.SkippedGenericFunction ? CheckMode.SkipGenericFunctions : CheckMode.Normal;
2157721573
}
2157821574
checkCandidate = getSignatureInstantiation(candidate, typeArgumentTypes, isInJSFile(candidate.declaration), inferenceContext && inferenceContext.inferredTypeParameters);
@@ -21746,7 +21742,7 @@ namespace ts {
2174621742
return maxParamsIndex;
2174721743
}
2174821744

21749-
function resolveCallExpression(node: CallExpression, candidatesOutArray: Signature[] | undefined, checkMode: CheckMode): Signature {
21745+
function resolveCallExpression(node: CallExpression, candidatesOutArray: Signature[] | undefined, checkMode: CheckMode, contextFlags?: ContextFlags): Signature {
2175021746
if (node.expression.kind === SyntaxKind.SuperKeyword) {
2175121747
const superType = checkSuperExpression(node.expression);
2175221748
if (isTypeAny(superType)) {
@@ -21842,7 +21838,7 @@ namespace ts {
2184221838
error(node, Diagnostics.Value_of_type_0_is_not_callable_Did_you_mean_to_include_new, typeToString(funcType));
2184321839
return resolveErrorCall(node);
2184421840
}
21845-
return resolveCall(node, callSignatures, candidatesOutArray, checkMode);
21841+
return resolveCall(node, callSignatures, candidatesOutArray, checkMode, /*fallbackError*/ undefined, contextFlags);
2184621842
}
2184721843

2184821844
function isGenericFunctionReturningFunction(signature: Signature) {
@@ -22181,10 +22177,10 @@ namespace ts {
2218122177
signature.parameters.length < getDecoratorArgumentCount(decorator, signature));
2218222178
}
2218322179

22184-
function resolveSignature(node: CallLikeExpression, candidatesOutArray: Signature[] | undefined, checkMode: CheckMode): Signature {
22180+
function resolveSignature(node: CallLikeExpression, candidatesOutArray: Signature[] | undefined, checkMode: CheckMode, contextFlags?: ContextFlags): Signature {
2218522181
switch (node.kind) {
2218622182
case SyntaxKind.CallExpression:
22187-
return resolveCallExpression(node, candidatesOutArray, checkMode);
22183+
return resolveCallExpression(node, candidatesOutArray, checkMode, contextFlags);
2218822184
case SyntaxKind.NewExpression:
2218922185
return resolveNewExpression(node, candidatesOutArray, checkMode);
2219022186
case SyntaxKind.TaggedTemplateExpression:
@@ -22205,7 +22201,7 @@ namespace ts {
2220522201
* the function will fill it up with appropriate candidate signatures
2220622202
* @return a signature of the call-like expression or undefined if one can't be found
2220722203
*/
22208-
function getResolvedSignature(node: CallLikeExpression, candidatesOutArray?: Signature[] | undefined, checkMode?: CheckMode): Signature {
22204+
function getResolvedSignature(node: CallLikeExpression, candidatesOutArray?: Signature[] | undefined, checkMode?: CheckMode, contextFlags?: ContextFlags): Signature {
2220922205
const links = getNodeLinks(node);
2221022206
// If getResolvedSignature has already been called, we will have cached the resolvedSignature.
2221122207
// However, it is possible that either candidatesOutArray was not passed in the first time,
@@ -22216,7 +22212,7 @@ namespace ts {
2221622212
return cached;
2221722213
}
2221822214
links.resolvedSignature = resolvingSignature;
22219-
const result = resolveSignature(node, candidatesOutArray, checkMode || CheckMode.Normal);
22215+
const result = resolveSignature(node, candidatesOutArray, checkMode || CheckMode.Normal, contextFlags);
2222022216
// When CheckMode.SkipGenericFunctions is set we use resolvingSignature to indicate that call
2222122217
// resolution should be deferred.
2222222218
if (result !== resolvingSignature) {

src/compiler/types.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3174,7 +3174,7 @@ namespace ts {
31743174
getFullyQualifiedName(symbol: Symbol): string;
31753175
getAugmentedPropertiesOfType(type: Type): Symbol[];
31763176
getRootSymbols(symbol: Symbol): ReadonlyArray<Symbol>;
3177-
getContextualType(node: Expression): Type | undefined;
3177+
getContextualType(node: Expression, contextFlags?: ContextFlags): Type | undefined;
31783178
/* @internal */ getContextualTypeForObjectLiteralElement(element: ObjectLiteralElementLike): Type | undefined;
31793179
/* @internal */ getContextualTypeForArgumentAtIndex(call: CallLikeExpression, argIndex: number): Type | undefined;
31803180
/* @internal */ getContextualTypeForJsxAttribute(attribute: JsxAttribute | JsxSpreadAttribute): Type | undefined;
@@ -3332,6 +3332,12 @@ namespace ts {
33323332
Subtype
33333333
}
33343334

3335+
export const enum ContextFlags {
3336+
None = 0,
3337+
Signature = 1 << 0, // Obtaining contextual signature
3338+
Completion = 1 << 1, // Obtaining constraint type for completion
3339+
}
3340+
33353341
// NOTE: If modifying this enum, must modify `TypeFormatFlags` too!
33363342
export const enum NodeBuilderFlags {
33373343
None = 0,

src/services/completions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1454,7 +1454,7 @@ namespace ts.Completions {
14541454
let existingMembers: ReadonlyArray<Declaration> | undefined;
14551455

14561456
if (objectLikeContainer.kind === SyntaxKind.ObjectLiteralExpression) {
1457-
const typeForObject = typeChecker.getContextualType(objectLikeContainer);
1457+
const typeForObject = typeChecker.getContextualType(objectLikeContainer, ContextFlags.Completion);
14581458
if (!typeForObject) return GlobalsSearch.Fail;
14591459
isNewIdentifierLocation = hasIndexSignature(typeForObject);
14601460
typeMembers = getPropertiesForObjectExpression(typeForObject, objectLikeContainer, typeChecker);

tests/baselines/reference/api/tsserverlibrary.d.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1980,7 +1980,7 @@ declare namespace ts {
19801980
getFullyQualifiedName(symbol: Symbol): string;
19811981
getAugmentedPropertiesOfType(type: Type): Symbol[];
19821982
getRootSymbols(symbol: Symbol): ReadonlyArray<Symbol>;
1983-
getContextualType(node: Expression): Type | undefined;
1983+
getContextualType(node: Expression, contextFlags?: ContextFlags): Type | undefined;
19841984
/**
19851985
* returns unknownSignature in the case of an error.
19861986
* returns undefined if the node is not valid.
@@ -2011,6 +2011,11 @@ declare namespace ts {
20112011
*/
20122012
runWithCancellationToken<T>(token: CancellationToken, cb: (checker: TypeChecker) => T): T;
20132013
}
2014+
enum ContextFlags {
2015+
None = 0,
2016+
Signature = 1,
2017+
Completion = 2
2018+
}
20142019
enum NodeBuilderFlags {
20152020
None = 0,
20162021
NoTruncation = 1,

tests/baselines/reference/api/typescript.d.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1980,7 +1980,7 @@ declare namespace ts {
19801980
getFullyQualifiedName(symbol: Symbol): string;
19811981
getAugmentedPropertiesOfType(type: Type): Symbol[];
19821982
getRootSymbols(symbol: Symbol): ReadonlyArray<Symbol>;
1983-
getContextualType(node: Expression): Type | undefined;
1983+
getContextualType(node: Expression, contextFlags?: ContextFlags): Type | undefined;
19841984
/**
19851985
* returns unknownSignature in the case of an error.
19861986
* returns undefined if the node is not valid.
@@ -2011,6 +2011,11 @@ declare namespace ts {
20112011
*/
20122012
runWithCancellationToken<T>(token: CancellationToken, cb: (checker: TypeChecker) => T): T;
20132013
}
2014+
enum ContextFlags {
2015+
None = 0,
2016+
Signature = 1,
2017+
Completion = 2
2018+
}
20142019
enum NodeBuilderFlags {
20152020
None = 0,
20162021
NoTruncation = 1,
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/// <reference path="fourslash.ts" />
2+
// @strict: true
3+
4+
//// interface MyOptions {
5+
//// hello?: boolean;
6+
//// world?: boolean;
7+
//// }
8+
//// declare function bar<T extends MyOptions>(options?: Partial<T>): void;
9+
//// bar({ hello, /*1*/ });
10+
11+
verify.completions({ marker: '1', includes: ['world'] })
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/// <reference path="fourslash.ts" />
2+
// @strict: true
3+
4+
//// interface DeepOptions {
5+
//// another?: boolean;
6+
//// }
7+
//// interface MyOptions {
8+
//// hello?: boolean;
9+
//// world?: boolean;
10+
//// deep?: DeepOptions
11+
//// }
12+
//// declare function bar<T extends MyOptions>(options?: Partial<T>): void;
13+
//// bar({ deep: {/*1*/} });
14+
15+
verify.completions({ marker: '1', includes: ['another'] })

0 commit comments

Comments
 (0)