Skip to content

Commit 3d0e2f5

Browse files
committed
Introduce ContextFlags to propagate origin of request for contextual type
1 parent 7ec7f02 commit 3d0e2f5

File tree

1 file changed

+37
-33
lines changed

1 file changed

+37
-33
lines changed

src/compiler/checker.ts

Lines changed: 37 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,11 @@ namespace ts {
701701
IsForSignatureHelp = 1 << 4, // Call resolution for purposes of signature help
702702
}
703703

704+
const enum ContextFlags {
705+
None = 0,
706+
Signature = 1 << 0, // Obtaining contextual signature
707+
}
708+
704709
const enum CallbackCheck {
705710
None,
706711
Bivariant,
@@ -17751,7 +17756,7 @@ namespace ts {
1775117756
return undefined;
1775217757
}
1775317758

17754-
function getContextualTypeForBinaryOperand(node: Expression): Type | undefined {
17759+
function getContextualTypeForBinaryOperand(node: Expression, contextFlags?: ContextFlags): Type | undefined {
1775517760
const binaryExpression = <BinaryExpression>node.parent;
1775617761
const { left, operatorToken, right } = binaryExpression;
1775717762
switch (operatorToken.kind) {
@@ -17768,12 +17773,12 @@ namespace ts {
1776817773
// When an || expression has a contextual type, the operands are contextually typed by that type. When an ||
1776917774
// expression has no contextual type, the right operand is contextually typed by the type of the left operand,
1777017775
// except for the special case of Javascript declarations of the form `namespace.prop = namespace.prop || {}`
17771-
const type = getContextualType(binaryExpression);
17776+
const type = getContextualType(binaryExpression, contextFlags);
1777217777
return !type && node === right && !isDefaultedExpandoInitializer(binaryExpression) ?
1777317778
getTypeOfExpression(left) : type;
1777417779
case SyntaxKind.AmpersandAmpersandToken:
1777517780
case SyntaxKind.CommaToken:
17776-
return node === right ? getContextualType(binaryExpression) : undefined;
17781+
return node === right ? getContextualType(binaryExpression, contextFlags) : undefined;
1777717782
default:
1777817783
return undefined;
1777917784
}
@@ -17882,19 +17887,18 @@ namespace ts {
1788217887
// In an object literal contextually typed by a type T, the contextual type of a property assignment is the type of
1788317888
// the matching property in T, if one exists. Otherwise, it is the type of the numeric index signature in T, if one
1788417889
// exists. Otherwise, it is the type of the string index signature in T, if one exists.
17885-
function getContextualTypeForObjectLiteralMethod(node: MethodDeclaration): Type | undefined {
17890+
function getContextualTypeForObjectLiteralMethod(node: MethodDeclaration, contextFlags?: ContextFlags): Type | undefined {
1788617891
Debug.assert(isObjectLiteralMethod(node));
1788717892
if (node.flags & NodeFlags.InWithStatement) {
1788817893
// We cannot answer semantic questions within a with block, do not proceed any further
1788917894
return undefined;
1789017895
}
17891-
17892-
return getContextualTypeForObjectLiteralElement(node);
17896+
return getContextualTypeForObjectLiteralElement(node, contextFlags);
1789317897
}
1789417898

17895-
function getContextualTypeForObjectLiteralElement(element: ObjectLiteralElementLike) {
17899+
function getContextualTypeForObjectLiteralElement(element: ObjectLiteralElementLike, contextFlags?: ContextFlags) {
1789617900
const objectLiteral = <ObjectLiteralExpression>element.parent;
17897-
const type = getApparentTypeOfContextualType(objectLiteral);
17901+
const type = getApparentTypeOfContextualType(objectLiteral, contextFlags);
1789817902
if (type) {
1789917903
if (!hasNonBindableDynamicName(element)) {
1790017904
// For a (non-symbol) computed property, there is no reason to look up the name
@@ -17906,11 +17910,9 @@ namespace ts {
1790617910
return propertyType;
1790717911
}
1790817912
}
17909-
1791017913
return isNumericName(element.name!) && getIndexTypeOfContextualType(type, IndexKind.Number) ||
1791117914
getIndexTypeOfContextualType(type, IndexKind.String);
1791217915
}
17913-
1791417916
return undefined;
1791517917
}
1791617918

@@ -17925,9 +17927,9 @@ namespace ts {
1792517927
}
1792617928

1792717929
// In a contextually typed conditional expression, the true/false expressions are contextually typed by the same type.
17928-
function getContextualTypeForConditionalOperand(node: Expression): Type | undefined {
17930+
function getContextualTypeForConditionalOperand(node: Expression, contextFlags?: ContextFlags): Type | undefined {
1792917931
const conditional = <ConditionalExpression>node.parent;
17930-
return node === conditional.whenTrue || node === conditional.whenFalse ? getContextualType(conditional) : undefined;
17932+
return node === conditional.whenTrue || node === conditional.whenFalse ? getContextualType(conditional, contextFlags) : undefined;
1793117933
}
1793217934

1793317935
function getContextualTypeForChildJsxExpression(node: JsxElement, child: JsxChild) {
@@ -18022,8 +18024,8 @@ namespace ts {
1802218024

1802318025
// Return the contextual type for a given expression node. During overload resolution, a contextual type may temporarily
1802418026
// be "pushed" onto a node using the contextualType property.
18025-
function getApparentTypeOfContextualType(node: Expression): Type | undefined {
18026-
const contextualType = instantiateContextualType(getContextualType(node), node);
18027+
function getApparentTypeOfContextualType(node: Expression, contextFlags?: ContextFlags): Type | undefined {
18028+
const contextualType = instantiateContextualType(getContextualType(node, contextFlags), node, contextFlags);
1802718029
if (contextualType) {
1802818030
const apparentType = mapType(contextualType, getApparentType, /*noReductions*/ true);
1802918031
if (apparentType.flags & TypeFlags.Union) {
@@ -18040,13 +18042,19 @@ namespace ts {
1804018042

1804118043
// If the given contextual type contains instantiable types and if a mapper representing
1804218044
// return type inferences is available, instantiate those types using that mapper.
18043-
function instantiateContextualType(contextualType: Type | undefined, node: Expression): Type | undefined {
18045+
function instantiateContextualType(contextualType: Type | undefined, node: Expression, contextFlags?: ContextFlags): Type | undefined {
1804418046
if (contextualType && maybeTypeOfKind(contextualType, TypeFlags.Instantiable)) {
1804518047
const inferenceContext = getInferenceContext(node);
18046-
if (inferenceContext) {
18047-
if ((isFunctionExpressionOrArrowFunction(node) || isObjectLiteralMethod(node)) && isContextSensitive(node)) {
18048+
// If no inferences have been made, nothing is gained from instantiating as type parameters
18049+
// would just be replaced with their defaults similar to the apparent type.
18050+
if (inferenceContext && some(inferenceContext.inferences, hasInferenceCandidates)) {
18051+
// For contextual signatures we incorporate all inferences made so far, e.g. from return
18052+
// types as well as arguments to the left in a function call.
18053+
if (contextFlags && contextFlags & ContextFlags.Signature) {
1804818054
return instantiateInstantiableTypes(contextualType, inferenceContext.nonFixingMapper);
1804918055
}
18056+
// For other purposes (e.g. determining whether to produce literal types) we only
18057+
// incorporate inferences made from the return type in a function call.
1805018058
if (inferenceContext.returnMapper) {
1805118059
return instantiateInstantiableTypes(contextualType, inferenceContext.returnMapper);
1805218060
}
@@ -18088,7 +18096,7 @@ namespace ts {
1808818096
* @param node the expression whose contextual type will be returned.
1808918097
* @returns the contextual type of an expression.
1809018098
*/
18091-
function getContextualType(node: Expression): Type | undefined {
18099+
function getContextualType(node: Expression, contextFlags?: ContextFlags): Type | undefined {
1809218100
if (node.flags & NodeFlags.InWithStatement) {
1809318101
// We cannot answer semantic questions within a with block, do not proceed any further
1809418102
return undefined;
@@ -18118,26 +18126,26 @@ namespace ts {
1811818126
case SyntaxKind.AsExpression:
1811918127
return isConstTypeReference((<AssertionExpression>parent).type) ? undefined : getTypeFromTypeNode((<AssertionExpression>parent).type);
1812018128
case SyntaxKind.BinaryExpression:
18121-
return getContextualTypeForBinaryOperand(node);
18129+
return getContextualTypeForBinaryOperand(node, contextFlags);
1812218130
case SyntaxKind.PropertyAssignment:
1812318131
case SyntaxKind.ShorthandPropertyAssignment:
18124-
return getContextualTypeForObjectLiteralElement(<PropertyAssignment | ShorthandPropertyAssignment>parent);
18132+
return getContextualTypeForObjectLiteralElement(<PropertyAssignment | ShorthandPropertyAssignment>parent, contextFlags);
1812518133
case SyntaxKind.SpreadAssignment:
18126-
return getApparentTypeOfContextualType(parent.parent as ObjectLiteralExpression);
18134+
return getApparentTypeOfContextualType(parent.parent as ObjectLiteralExpression, contextFlags);
1812718135
case SyntaxKind.ArrayLiteralExpression: {
1812818136
const arrayLiteral = <ArrayLiteralExpression>parent;
18129-
const type = getApparentTypeOfContextualType(arrayLiteral);
18137+
const type = getApparentTypeOfContextualType(arrayLiteral, contextFlags);
1813018138
return getContextualTypeForElementExpression(type, indexOfNode(arrayLiteral.elements, node));
1813118139
}
1813218140
case SyntaxKind.ConditionalExpression:
18133-
return getContextualTypeForConditionalOperand(node);
18141+
return getContextualTypeForConditionalOperand(node, contextFlags);
1813418142
case SyntaxKind.TemplateSpan:
1813518143
Debug.assert(parent.parent.kind === SyntaxKind.TemplateExpression);
1813618144
return getContextualTypeForSubstitutionExpression(<TemplateExpression>parent.parent, node);
1813718145
case SyntaxKind.ParenthesizedExpression: {
1813818146
// Like in `checkParenthesizedExpression`, an `/** @type {xyz} */` comment before a parenthesized expression acts as a type cast.
1813918147
const tag = isInJSFile(parent) ? getJSDocTypeTag(parent) : undefined;
18140-
return tag ? getTypeFromTypeNode(tag.typeExpression!.type) : getContextualType(<ParenthesizedExpression>parent);
18148+
return tag ? getTypeFromTypeNode(tag.typeExpression!.type) : getContextualType(<ParenthesizedExpression>parent, contextFlags);
1814118149
}
1814218150
case SyntaxKind.JsxExpression:
1814318151
return getContextualTypeForJsxExpression(<JsxExpression>parent);
@@ -18328,12 +18336,6 @@ namespace ts {
1832818336
: undefined;
1832918337
}
1833018338

18331-
function getContextualTypeForFunctionLikeDeclaration(node: FunctionExpression | ArrowFunction | MethodDeclaration) {
18332-
return isObjectLiteralMethod(node) ?
18333-
getContextualTypeForObjectLiteralMethod(node) :
18334-
getApparentTypeOfContextualType(node);
18335-
}
18336-
1833718339
// Return the contextual signature for a given expression node. A contextual type provides a
1833818340
// contextual signature if it has a single call signature and if that call signature is non-generic.
1833918341
// If the contextual type is a union type, get the signature from each type possible and if they are
@@ -18345,7 +18347,9 @@ namespace ts {
1834518347
if (typeTagSignature) {
1834618348
return typeTagSignature;
1834718349
}
18348-
const type = getContextualTypeForFunctionLikeDeclaration(node);
18350+
const type = isObjectLiteralMethod(node) ?
18351+
getContextualTypeForObjectLiteralMethod(node, ContextFlags.Signature) :
18352+
getApparentTypeOfContextualType(node, ContextFlags.Signature);
1834918353
if (!type) {
1835018354
return undefined;
1835118355
}
@@ -20249,7 +20253,7 @@ namespace ts {
2024920253
return type;
2025020254
}
2025120255

20252-
function getSpreadArgumentType(args: ReadonlyArray<Expression>, index: number, argCount: number, restType: TypeParameter, context: InferenceContext | undefined) {
20256+
function getSpreadArgumentType(args: ReadonlyArray<Expression>, index: number, argCount: number, restType: Type, context: InferenceContext | undefined) {
2025320257
if (index >= argCount - 1) {
2025420258
const arg = args[argCount - 1];
2025520259
if (isSpreadArgument(arg)) {
@@ -20260,7 +20264,7 @@ namespace ts {
2026020264
getArrayifiedType(checkExpressionWithContextualType((<SpreadElement>arg).expression, restType, context, CheckMode.Normal));
2026120265
}
2026220266
}
20263-
const contextualType = getIndexTypeOfType(restType, IndexKind.Number) || anyType;
20267+
const contextualType = getIndexedAccessType(restType, numberType);
2026420268
const hasPrimitiveContextualType = maybeTypeOfKind(contextualType, TypeFlags.Primitive | TypeFlags.Index);
2026520269
const types = [];
2026620270
let spreadIndex = -1;

0 commit comments

Comments
 (0)