Improve contextual typing of partially annotated signatures

This commit is contained in:
Anders Hejlsberg 2016-10-16 17:28:45 -07:00
parent 65b1cf665e
commit 8094b2c5de

View File

@ -3144,8 +3144,7 @@ namespace ts {
// Use contextual parameter type if one is available
let type: Type;
if (declaration.symbol.name === "this") {
const thisParameter = getContextualThisParameter(func);
type = thisParameter ? getTypeOfSymbol(thisParameter) : undefined;
type = getContextualThisParameterType(func);
}
else {
type = getContextuallyTypedParameterType(<ParameterDeclaration>declaration);
@ -4789,9 +4788,6 @@ namespace ts {
if (isJSConstructSignature) {
minArgumentCount--;
}
if (!thisParameter && isObjectLiteralMethod(declaration)) {
thisParameter = getContextualThisParameter(declaration);
}
const classType = declaration.kind === SyntaxKind.Constructor ?
getDeclaredTypeOfClassOrInterface(getMergedSymbol((<ClassDeclaration>declaration.parent).symbol))
@ -6101,9 +6097,24 @@ namespace ts {
}
function isContextSensitiveFunctionLikeDeclaration(node: FunctionLikeDeclaration) {
const areAllParametersUntyped = !forEach(node.parameters, p => p.type);
const isNullaryArrow = node.kind === SyntaxKind.ArrowFunction && !node.parameters.length;
return !node.typeParameters && areAllParametersUntyped && !isNullaryArrow;
// Functions with type parameters are not context sensitive.
if (node.typeParameters) {
return false;
}
// Functions with any parameters that lack type annotations are context sensitive.
if (forEach(node.parameters, p => !p.type)) {
return true;
}
// For arrow functions we now know we're not context sensitive.
if (node.kind === SyntaxKind.ArrowFunction) {
return false;
}
// If the first parameter is not an explicit 'this' parameter, then the function has
// an implicit 'this' parameter which is subject to contextual typing. Otherwise we
// know that all parameters (including 'this') have type annotations and nothing is
// subject to contextual typing.
const parameter = firstOrUndefined(node.parameters);
return !(parameter && parameter.name.kind === SyntaxKind.Identifier && (<Identifier>parameter.name).text === "this");
}
function isContextSensitiveFunctionOrObjectLiteralMethod(func: Node): func is FunctionExpression | ArrowFunction | MethodDeclaration {
@ -9629,7 +9640,7 @@ namespace ts {
}
}
const thisType = getThisTypeOfDeclaration(container);
let thisType = getThisTypeOfDeclaration(container) || getContextualThisParameterType(container);
if (thisType) {
return thisType;
}
@ -9869,14 +9880,16 @@ namespace ts {
}
}
function getContextualThisParameter(func: FunctionLikeDeclaration): Symbol {
function getContextualThisParameterType(func: FunctionLikeDeclaration): Type {
if (isContextSensitiveFunctionOrObjectLiteralMethod(func) && func.kind !== SyntaxKind.ArrowFunction) {
const contextualSignature = getContextualSignature(func);
if (contextualSignature) {
return contextualSignature.thisParameter;
const thisParameter = contextualSignature.thisParameter;
if (thisParameter) {
return getTypeOfSymbol(thisParameter);
}
}
}
return undefined;
}
@ -12840,21 +12853,36 @@ namespace ts {
function assignContextualParameterTypes(signature: Signature, context: Signature, mapper: TypeMapper) {
const len = signature.parameters.length - (signature.hasRestParameter ? 1 : 0);
if (context.thisParameter) {
if (!signature.thisParameter) {
signature.thisParameter = createTransientSymbol(context.thisParameter, undefined);
if (isInferentialContext(mapper)) {
for (let i = 0; i < len; i++) {
const declaration = <ParameterDeclaration>signature.parameters[i].valueDeclaration;
if (declaration.type) {
inferTypes(mapper.context, getTypeFromTypeNode(declaration.type), getTypeAtPosition(context, i));
}
}
}
if (context.thisParameter) {
const parameter = signature.thisParameter;
if (!parameter || parameter.valueDeclaration && !(<ParameterDeclaration>parameter.valueDeclaration).type) {
if (!parameter) {
signature.thisParameter = createTransientSymbol(context.thisParameter, undefined);
}
assignTypeToParameterAndFixTypeParameters(signature.thisParameter, getTypeOfSymbol(context.thisParameter), mapper);
}
assignTypeToParameterAndFixTypeParameters(signature.thisParameter, getTypeOfSymbol(context.thisParameter), mapper);
}
for (let i = 0; i < len; i++) {
const parameter = signature.parameters[i];
const contextualParameterType = getTypeAtPosition(context, i);
assignTypeToParameterAndFixTypeParameters(parameter, contextualParameterType, mapper);
if (!(<ParameterDeclaration>parameter.valueDeclaration).type) {
const contextualParameterType = getTypeAtPosition(context, i);
assignTypeToParameterAndFixTypeParameters(parameter, contextualParameterType, mapper);
}
}
if (signature.hasRestParameter && isRestParameterIndex(context, signature.parameters.length - 1)) {
const parameter = lastOrUndefined(signature.parameters);
const contextualParameterType = getTypeOfSymbol(lastOrUndefined(context.parameters));
assignTypeToParameterAndFixTypeParameters(parameter, contextualParameterType, mapper);
if (!(<ParameterDeclaration>parameter.valueDeclaration).type) {
const contextualParameterType = getTypeOfSymbol(lastOrUndefined(context.parameters));
assignTypeToParameterAndFixTypeParameters(parameter, contextualParameterType, mapper);
}
}
}