Introduce ContextFlags to propagate origin of request for contextual type

This commit is contained in:
Anders Hejlsberg 2019-03-24 11:15:51 -07:00
parent 7ec7f02ec3
commit 3d0e2f5f45

View File

@ -701,6 +701,11 @@ namespace ts {
IsForSignatureHelp = 1 << 4, // Call resolution for purposes of signature help
}
const enum ContextFlags {
None = 0,
Signature = 1 << 0, // Obtaining contextual signature
}
const enum CallbackCheck {
None,
Bivariant,
@ -17751,7 +17756,7 @@ namespace ts {
return undefined;
}
function getContextualTypeForBinaryOperand(node: Expression): Type | undefined {
function getContextualTypeForBinaryOperand(node: Expression, contextFlags?: ContextFlags): Type | undefined {
const binaryExpression = <BinaryExpression>node.parent;
const { left, operatorToken, right } = binaryExpression;
switch (operatorToken.kind) {
@ -17768,12 +17773,12 @@ namespace ts {
// When an || expression has a contextual type, the operands are contextually typed by that type. When an ||
// expression has no contextual type, the right operand is contextually typed by the type of the left operand,
// except for the special case of Javascript declarations of the form `namespace.prop = namespace.prop || {}`
const type = getContextualType(binaryExpression);
const type = getContextualType(binaryExpression, contextFlags);
return !type && node === right && !isDefaultedExpandoInitializer(binaryExpression) ?
getTypeOfExpression(left) : type;
case SyntaxKind.AmpersandAmpersandToken:
case SyntaxKind.CommaToken:
return node === right ? getContextualType(binaryExpression) : undefined;
return node === right ? getContextualType(binaryExpression, contextFlags) : undefined;
default:
return undefined;
}
@ -17882,19 +17887,18 @@ namespace ts {
// In an object literal contextually typed by a type T, the contextual type of a property assignment is the type of
// the matching property in T, if one exists. Otherwise, it is the type of the numeric index signature in T, if one
// exists. Otherwise, it is the type of the string index signature in T, if one exists.
function getContextualTypeForObjectLiteralMethod(node: MethodDeclaration): Type | undefined {
function getContextualTypeForObjectLiteralMethod(node: MethodDeclaration, contextFlags?: ContextFlags): Type | undefined {
Debug.assert(isObjectLiteralMethod(node));
if (node.flags & NodeFlags.InWithStatement) {
// We cannot answer semantic questions within a with block, do not proceed any further
return undefined;
}
return getContextualTypeForObjectLiteralElement(node);
return getContextualTypeForObjectLiteralElement(node, contextFlags);
}
function getContextualTypeForObjectLiteralElement(element: ObjectLiteralElementLike) {
function getContextualTypeForObjectLiteralElement(element: ObjectLiteralElementLike, contextFlags?: ContextFlags) {
const objectLiteral = <ObjectLiteralExpression>element.parent;
const type = getApparentTypeOfContextualType(objectLiteral);
const type = getApparentTypeOfContextualType(objectLiteral, contextFlags);
if (type) {
if (!hasNonBindableDynamicName(element)) {
// For a (non-symbol) computed property, there is no reason to look up the name
@ -17906,11 +17910,9 @@ namespace ts {
return propertyType;
}
}
return isNumericName(element.name!) && getIndexTypeOfContextualType(type, IndexKind.Number) ||
getIndexTypeOfContextualType(type, IndexKind.String);
}
return undefined;
}
@ -17925,9 +17927,9 @@ namespace ts {
}
// In a contextually typed conditional expression, the true/false expressions are contextually typed by the same type.
function getContextualTypeForConditionalOperand(node: Expression): Type | undefined {
function getContextualTypeForConditionalOperand(node: Expression, contextFlags?: ContextFlags): Type | undefined {
const conditional = <ConditionalExpression>node.parent;
return node === conditional.whenTrue || node === conditional.whenFalse ? getContextualType(conditional) : undefined;
return node === conditional.whenTrue || node === conditional.whenFalse ? getContextualType(conditional, contextFlags) : undefined;
}
function getContextualTypeForChildJsxExpression(node: JsxElement, child: JsxChild) {
@ -18022,8 +18024,8 @@ namespace ts {
// Return the contextual type for a given expression node. During overload resolution, a contextual type may temporarily
// be "pushed" onto a node using the contextualType property.
function getApparentTypeOfContextualType(node: Expression): Type | undefined {
const contextualType = instantiateContextualType(getContextualType(node), node);
function getApparentTypeOfContextualType(node: Expression, contextFlags?: ContextFlags): Type | undefined {
const contextualType = instantiateContextualType(getContextualType(node, contextFlags), node, contextFlags);
if (contextualType) {
const apparentType = mapType(contextualType, getApparentType, /*noReductions*/ true);
if (apparentType.flags & TypeFlags.Union) {
@ -18040,13 +18042,19 @@ namespace ts {
// If the given contextual type contains instantiable types and if a mapper representing
// return type inferences is available, instantiate those types using that mapper.
function instantiateContextualType(contextualType: Type | undefined, node: Expression): Type | undefined {
function instantiateContextualType(contextualType: Type | undefined, node: Expression, contextFlags?: ContextFlags): Type | undefined {
if (contextualType && maybeTypeOfKind(contextualType, TypeFlags.Instantiable)) {
const inferenceContext = getInferenceContext(node);
if (inferenceContext) {
if ((isFunctionExpressionOrArrowFunction(node) || isObjectLiteralMethod(node)) && isContextSensitive(node)) {
// If no inferences have been made, nothing is gained from instantiating as type parameters
// would just be replaced with their defaults similar to the apparent type.
if (inferenceContext && some(inferenceContext.inferences, hasInferenceCandidates)) {
// For contextual signatures we incorporate all inferences made so far, e.g. from return
// types as well as arguments to the left in a function call.
if (contextFlags && contextFlags & ContextFlags.Signature) {
return instantiateInstantiableTypes(contextualType, inferenceContext.nonFixingMapper);
}
// For other purposes (e.g. determining whether to produce literal types) we only
// incorporate inferences made from the return type in a function call.
if (inferenceContext.returnMapper) {
return instantiateInstantiableTypes(contextualType, inferenceContext.returnMapper);
}
@ -18088,7 +18096,7 @@ namespace ts {
* @param node the expression whose contextual type will be returned.
* @returns the contextual type of an expression.
*/
function getContextualType(node: Expression): Type | undefined {
function getContextualType(node: Expression, contextFlags?: ContextFlags): Type | undefined {
if (node.flags & NodeFlags.InWithStatement) {
// We cannot answer semantic questions within a with block, do not proceed any further
return undefined;
@ -18118,26 +18126,26 @@ namespace ts {
case SyntaxKind.AsExpression:
return isConstTypeReference((<AssertionExpression>parent).type) ? undefined : getTypeFromTypeNode((<AssertionExpression>parent).type);
case SyntaxKind.BinaryExpression:
return getContextualTypeForBinaryOperand(node);
return getContextualTypeForBinaryOperand(node, contextFlags);
case SyntaxKind.PropertyAssignment:
case SyntaxKind.ShorthandPropertyAssignment:
return getContextualTypeForObjectLiteralElement(<PropertyAssignment | ShorthandPropertyAssignment>parent);
return getContextualTypeForObjectLiteralElement(<PropertyAssignment | ShorthandPropertyAssignment>parent, contextFlags);
case SyntaxKind.SpreadAssignment:
return getApparentTypeOfContextualType(parent.parent as ObjectLiteralExpression);
return getApparentTypeOfContextualType(parent.parent as ObjectLiteralExpression, contextFlags);
case SyntaxKind.ArrayLiteralExpression: {
const arrayLiteral = <ArrayLiteralExpression>parent;
const type = getApparentTypeOfContextualType(arrayLiteral);
const type = getApparentTypeOfContextualType(arrayLiteral, contextFlags);
return getContextualTypeForElementExpression(type, indexOfNode(arrayLiteral.elements, node));
}
case SyntaxKind.ConditionalExpression:
return getContextualTypeForConditionalOperand(node);
return getContextualTypeForConditionalOperand(node, contextFlags);
case SyntaxKind.TemplateSpan:
Debug.assert(parent.parent.kind === SyntaxKind.TemplateExpression);
return getContextualTypeForSubstitutionExpression(<TemplateExpression>parent.parent, node);
case SyntaxKind.ParenthesizedExpression: {
// Like in `checkParenthesizedExpression`, an `/** @type {xyz} */` comment before a parenthesized expression acts as a type cast.
const tag = isInJSFile(parent) ? getJSDocTypeTag(parent) : undefined;
return tag ? getTypeFromTypeNode(tag.typeExpression!.type) : getContextualType(<ParenthesizedExpression>parent);
return tag ? getTypeFromTypeNode(tag.typeExpression!.type) : getContextualType(<ParenthesizedExpression>parent, contextFlags);
}
case SyntaxKind.JsxExpression:
return getContextualTypeForJsxExpression(<JsxExpression>parent);
@ -18328,12 +18336,6 @@ namespace ts {
: undefined;
}
function getContextualTypeForFunctionLikeDeclaration(node: FunctionExpression | ArrowFunction | MethodDeclaration) {
return isObjectLiteralMethod(node) ?
getContextualTypeForObjectLiteralMethod(node) :
getApparentTypeOfContextualType(node);
}
// Return the contextual signature for a given expression node. A contextual type provides a
// contextual signature if it has a single call signature and if that call signature is non-generic.
// 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 {
if (typeTagSignature) {
return typeTagSignature;
}
const type = getContextualTypeForFunctionLikeDeclaration(node);
const type = isObjectLiteralMethod(node) ?
getContextualTypeForObjectLiteralMethod(node, ContextFlags.Signature) :
getApparentTypeOfContextualType(node, ContextFlags.Signature);
if (!type) {
return undefined;
}
@ -20249,7 +20253,7 @@ namespace ts {
return type;
}
function getSpreadArgumentType(args: ReadonlyArray<Expression>, index: number, argCount: number, restType: TypeParameter, context: InferenceContext | undefined) {
function getSpreadArgumentType(args: ReadonlyArray<Expression>, index: number, argCount: number, restType: Type, context: InferenceContext | undefined) {
if (index >= argCount - 1) {
const arg = args[argCount - 1];
if (isSpreadArgument(arg)) {
@ -20260,7 +20264,7 @@ namespace ts {
getArrayifiedType(checkExpressionWithContextualType((<SpreadElement>arg).expression, restType, context, CheckMode.Normal));
}
}
const contextualType = getIndexTypeOfType(restType, IndexKind.Number) || anyType;
const contextualType = getIndexedAccessType(restType, numberType);
const hasPrimitiveContextualType = maybeTypeOfKind(contextualType, TypeFlags.Primitive | TypeFlags.Index);
const types = [];
let spreadIndex = -1;