diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3d74190b14f..8ef44e6ecdb 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3432,6 +3432,44 @@ namespace ts { return unknownType; } + function getTypeOfBasePropertyDeclaration(declaration: PropertyDeclaration) { + if (declaration.parent.kind === SyntaxKind.ClassDeclaration) { + const parent = declaration.parent; + const propertyName = declaration.symbol.name; + const extendedPropertyType = getSinglePropertyTypeOfTypes(getBaseTypes(getTypeOfSymbol(getSymbolOfNode(parent))), propertyName); + if (extendedPropertyType) { + return extendedPropertyType; + } + const implementedTypeNodes = getClassImplementsHeritageClauseElements(parent); + if (implementedTypeNodes) { + return getSinglePropertyTypeOfTypes(map(implementedTypeNodes, getTypeFromTypeReference), propertyName); + } + } + + return undefined; + } + + function getSinglePropertyTypeOfTypes(types: Type[], propertyName: string) { + let result: Type; + for (const t of types) { + if (t !== unknownType) { + const property = getPropertyOfType(t, propertyName); + if (!property || property.valueDeclaration.flags & NodeFlags.Private) { + continue; + } + if (property.name === propertyName) { + const propertyType = getTypeOfSymbol(property); + if (result && result !== propertyType) { + // if there's more than one matching property, return undefined + return undefined; + } + result = propertyType; + } + } + } + return result; + } + function getTargetType(type: ObjectType): Type { return type.flags & TypeFlags.Reference ? (type).target : type; } @@ -5898,7 +5936,7 @@ namespace ts { // Returns true if the given expression contains (at any level of nesting) a function or arrow expression // that is subject to contextual typing. function isContextSensitive(node: Expression | MethodDeclaration | ObjectLiteralElement): boolean { - Debug.assert(node.kind !== SyntaxKind.MethodDeclaration || isObjectLiteralMethod(node)); + Debug.assert(node.kind !== SyntaxKind.MethodDeclaration || isMethod(node)); switch (node.kind) { case SyntaxKind.FunctionExpression: case SyntaxKind.ArrowFunction: @@ -5931,8 +5969,8 @@ namespace ts { return !node.typeParameters && areAllParametersUntyped && !isNullaryArrow; } - function isContextSensitiveFunctionOrObjectLiteralMethod(func: Node): func is FunctionExpression | MethodDeclaration { - return (isFunctionExpressionOrArrowFunction(func) || isObjectLiteralMethod(func)) && isContextSensitiveFunctionLikeDeclaration(func); + function isContextSensitiveFunctionOrMethod(func: Node): func is FunctionExpression | MethodDeclaration { + return (isFunctionExpressionOrArrowFunction(func) || isMethod(func)) && isContextSensitiveFunctionLikeDeclaration(func); } function getTypeWithoutSignatures(type: Type): Type { @@ -9374,7 +9412,7 @@ namespace ts { } function getContextualThisParameter(func: FunctionLikeDeclaration): Symbol { - if (isContextSensitiveFunctionOrObjectLiteralMethod(func) && func.kind !== SyntaxKind.ArrowFunction) { + if (isContextSensitiveFunctionOrMethod(func) && func.kind !== SyntaxKind.ArrowFunction) { const contextualSignature = getContextualSignature(func); if (contextualSignature) { return contextualSignature.thisParameter; @@ -9387,7 +9425,7 @@ namespace ts { // Return contextual type of parameter or undefined if no contextual type is available function getContextuallyTypedParameterType(parameter: ParameterDeclaration): Type { const func = parameter.parent; - if (isContextSensitiveFunctionOrObjectLiteralMethod(func)) { + if (isContextSensitiveFunctionOrMethod(func)) { const iife = getImmediatelyInvokedFunctionExpression(func); if (iife) { const indexOfParameter = indexOf(func.parameters, parameter); @@ -9447,6 +9485,12 @@ namespace ts { return type; } } + if (declaration.kind === SyntaxKind.PropertyDeclaration) { + const type = getTypeOfBasePropertyDeclaration(declaration); + if (type) { + return type; + } + } if (isBindingPattern(declaration.name)) { return getTypeFromBindingPattern(declaration.name, /*includePatternInType*/ true, /*reportErrors*/ false); } @@ -9815,15 +9859,23 @@ namespace ts { function getContextualSignatureForFunctionLikeDeclaration(node: FunctionLikeDeclaration): Signature { // Only function expressions, arrow functions, and object literal methods are contextually typed. - return isFunctionExpressionOrArrowFunction(node) || isObjectLiteralMethod(node) + return isFunctionExpressionOrArrowFunction(node) || isMethod(node) ? getContextualSignature(node) : undefined; } function getContextualTypeForFunctionLikeDeclaration(node: FunctionExpression | MethodDeclaration) { - return isObjectLiteralMethod(node) ? - getContextualTypeForObjectLiteralMethod(node) : - getApparentTypeOfContextualType(node); + let type: Type; + if (isFunctionExpressionOrArrowFunction(node)) { + type = getApparentTypeOfContextualType(node); + } + else if (isObjectLiteralMethod(node)) { + type = getContextualTypeForObjectLiteralMethod(node); + } + else if (isMethod(node)) { + type = getTypeOfBasePropertyDeclaration(node); + } + return type; } // Return the contextual signature for a given expression node. A contextual type provides a @@ -9832,7 +9884,6 @@ namespace ts { // all identical ignoring their return type, the result is same signature but with return type as // union type of return types from these signatures function getContextualSignature(node: FunctionExpression | MethodDeclaration): Signature { - Debug.assert(node.kind !== SyntaxKind.MethodDeclaration || isObjectLiteralMethod(node)); const type = getContextualTypeForFunctionLikeDeclaration(node); if (!type) { return undefined; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 0c026fc6b6d..bd139abc9b1 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -864,6 +864,10 @@ namespace ts { return predicate && predicate.kind === TypePredicateKind.Identifier; } + export function isMethod(node: Node): node is MethodDeclaration { + return node && node.kind === SyntaxKind.MethodDeclaration; + } + export function isThisTypePredicate(predicate: TypePredicate): predicate is ThisTypePredicate { return predicate && predicate.kind === TypePredicateKind.This; }