Merge pull request #6111 from Microsoft/simplifyAbstractCheck

Simplify abstract constructor type assignability checking
This commit is contained in:
Anders Hejlsberg 2015-12-18 19:09:10 -08:00
commit 66a0f1d2ea

View File

@ -5549,32 +5549,24 @@ namespace ts {
if (target === anyFunctionType || source === anyFunctionType) {
return Ternary.True;
}
const sourceSignatures = getSignaturesOfType(source, kind);
const targetSignatures = getSignaturesOfType(target, kind);
if (kind === SignatureKind.Construct && sourceSignatures.length && targetSignatures.length &&
isAbstractConstructorType(source) && !isAbstractConstructorType(target)) {
// An abstract constructor type is not assignable to a non-abstract constructor type
// as it would otherwise be possible to new an abstract class. Note that the assignablity
// check we perform for an extends clause excludes construct signatures from the target,
// so this check never proceeds.
if (reportErrors) {
reportError(Diagnostics.Cannot_assign_an_abstract_constructor_type_to_a_non_abstract_constructor_type);
}
return Ternary.False;
}
let result = Ternary.True;
const saveErrorInfo = errorInfo;
if (kind === SignatureKind.Construct) {
// Only want to compare the construct signatures for abstractness guarantees.
// Because the "abstractness" of a class is the same across all construct signatures
// (internally we are checking the corresponding declaration), it is enough to perform
// the check and report an error once over all pairs of source and target construct signatures.
//
// sourceSig and targetSig are (possibly) undefined.
//
// Note that in an extends-clause, targetSignatures is stripped, so the check never proceeds.
const sourceSig = sourceSignatures[0];
const targetSig = targetSignatures[0];
result &= abstractSignatureRelatedTo(source, sourceSig, target, targetSig);
if (result !== Ternary.True) {
return result;
}
}
outer: for (const t of targetSignatures) {
if (!t.hasStringLiterals || target.flags & TypeFlags.FromSignature) {
// Only elaborate errors from the first failure
@ -5601,40 +5593,6 @@ namespace ts {
}
}
return result;
function abstractSignatureRelatedTo(source: Type, sourceSig: Signature, target: Type, targetSig: Signature) {
if (sourceSig && targetSig) {
const sourceDecl = source.symbol && getClassLikeDeclarationOfSymbol(source.symbol);
const targetDecl = target.symbol && getClassLikeDeclarationOfSymbol(target.symbol);
if (!sourceDecl) {
// If the source object isn't itself a class declaration, it can be freely assigned, regardless
// of whether the constructed object is abstract or not.
return Ternary.True;
}
const sourceErasedSignature = getErasedSignature(sourceSig);
const targetErasedSignature = getErasedSignature(targetSig);
const sourceReturnType = sourceErasedSignature && getReturnTypeOfSignature(sourceErasedSignature);
const targetReturnType = targetErasedSignature && getReturnTypeOfSignature(targetErasedSignature);
const sourceReturnDecl = sourceReturnType && sourceReturnType.symbol && getClassLikeDeclarationOfSymbol(sourceReturnType.symbol);
const targetReturnDecl = targetReturnType && targetReturnType.symbol && getClassLikeDeclarationOfSymbol(targetReturnType.symbol);
const sourceIsAbstract = sourceReturnDecl && sourceReturnDecl.flags & NodeFlags.Abstract;
const targetIsAbstract = targetReturnDecl && targetReturnDecl.flags & NodeFlags.Abstract;
if (sourceIsAbstract && !(targetIsAbstract && targetDecl)) {
// if target isn't a class-declaration type, then it can be new'd, so we forbid the assignment.
if (reportErrors) {
reportError(Diagnostics.Cannot_assign_an_abstract_constructor_type_to_a_non_abstract_constructor_type);
}
return Ternary.False;
}
}
return Ternary.True;
}
}
/**
@ -5830,6 +5788,20 @@ namespace ts {
}
}
// Return true if the given type is the constructor type for an abstract class
function isAbstractConstructorType(type: Type) {
if (type.flags & TypeFlags.Anonymous) {
const symbol = type.symbol;
if (symbol && symbol.flags & SymbolFlags.Class) {
const declaration = getClassLikeDeclarationOfSymbol(symbol);
if (declaration && declaration.flags & NodeFlags.Abstract) {
return true;
}
}
}
return false;
}
// Return true if the given type is part of a deeply nested chain of generic instantiations. We consider this to be the case
// when structural type comparisons have been started for 10 or more instantiations of the same generic type. It is possible,
// though highly unlikely, for this test to be true in a situation where a chain of instantiations is not infinitely expanding.