Skip to content

Commit 66a0f1d

Browse files
committed
Merge pull request #6111 from Microsoft/simplifyAbstractCheck
Simplify abstract constructor type assignability checking
2 parents dcd6927 + c21193a commit 66a0f1d

File tree

1 file changed

+27
-55
lines changed

1 file changed

+27
-55
lines changed

src/compiler/checker.ts

Lines changed: 27 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -5549,32 +5549,24 @@ namespace ts {
55495549
if (target === anyFunctionType || source === anyFunctionType) {
55505550
return Ternary.True;
55515551
}
5552+
55525553
const sourceSignatures = getSignaturesOfType(source, kind);
55535554
const targetSignatures = getSignaturesOfType(target, kind);
5554-
let result = Ternary.True;
5555-
const saveErrorInfo = errorInfo;
5556-
5557-
5558-
5559-
if (kind === SignatureKind.Construct) {
5560-
// Only want to compare the construct signatures for abstractness guarantees.
5561-
5562-
// Because the "abstractness" of a class is the same across all construct signatures
5563-
// (internally we are checking the corresponding declaration), it is enough to perform
5564-
// the check and report an error once over all pairs of source and target construct signatures.
5565-
//
5566-
// sourceSig and targetSig are (possibly) undefined.
5567-
//
5568-
// Note that in an extends-clause, targetSignatures is stripped, so the check never proceeds.
5569-
const sourceSig = sourceSignatures[0];
5570-
const targetSig = targetSignatures[0];
5571-
5572-
result &= abstractSignatureRelatedTo(source, sourceSig, target, targetSig);
5573-
if (result !== Ternary.True) {
5574-
return result;
5555+
if (kind === SignatureKind.Construct && sourceSignatures.length && targetSignatures.length &&
5556+
isAbstractConstructorType(source) && !isAbstractConstructorType(target)) {
5557+
// An abstract constructor type is not assignable to a non-abstract constructor type
5558+
// as it would otherwise be possible to new an abstract class. Note that the assignablity
5559+
// check we perform for an extends clause excludes construct signatures from the target,
5560+
// so this check never proceeds.
5561+
if (reportErrors) {
5562+
reportError(Diagnostics.Cannot_assign_an_abstract_constructor_type_to_a_non_abstract_constructor_type);
55755563
}
5564+
return Ternary.False;
55765565
}
55775566

5567+
let result = Ternary.True;
5568+
const saveErrorInfo = errorInfo;
5569+
55785570
outer: for (const t of targetSignatures) {
55795571
if (!t.hasStringLiterals || target.flags & TypeFlags.FromSignature) {
55805572
// Only elaborate errors from the first failure
@@ -5601,40 +5593,6 @@ namespace ts {
56015593
}
56025594
}
56035595
return result;
5604-
5605-
function abstractSignatureRelatedTo(source: Type, sourceSig: Signature, target: Type, targetSig: Signature) {
5606-
if (sourceSig && targetSig) {
5607-
5608-
const sourceDecl = source.symbol && getClassLikeDeclarationOfSymbol(source.symbol);
5609-
const targetDecl = target.symbol && getClassLikeDeclarationOfSymbol(target.symbol);
5610-
5611-
if (!sourceDecl) {
5612-
// If the source object isn't itself a class declaration, it can be freely assigned, regardless
5613-
// of whether the constructed object is abstract or not.
5614-
return Ternary.True;
5615-
}
5616-
5617-
const sourceErasedSignature = getErasedSignature(sourceSig);
5618-
const targetErasedSignature = getErasedSignature(targetSig);
5619-
5620-
const sourceReturnType = sourceErasedSignature && getReturnTypeOfSignature(sourceErasedSignature);
5621-
const targetReturnType = targetErasedSignature && getReturnTypeOfSignature(targetErasedSignature);
5622-
5623-
const sourceReturnDecl = sourceReturnType && sourceReturnType.symbol && getClassLikeDeclarationOfSymbol(sourceReturnType.symbol);
5624-
const targetReturnDecl = targetReturnType && targetReturnType.symbol && getClassLikeDeclarationOfSymbol(targetReturnType.symbol);
5625-
const sourceIsAbstract = sourceReturnDecl && sourceReturnDecl.flags & NodeFlags.Abstract;
5626-
const targetIsAbstract = targetReturnDecl && targetReturnDecl.flags & NodeFlags.Abstract;
5627-
5628-
if (sourceIsAbstract && !(targetIsAbstract && targetDecl)) {
5629-
// if target isn't a class-declaration type, then it can be new'd, so we forbid the assignment.
5630-
if (reportErrors) {
5631-
reportError(Diagnostics.Cannot_assign_an_abstract_constructor_type_to_a_non_abstract_constructor_type);
5632-
}
5633-
return Ternary.False;
5634-
}
5635-
}
5636-
return Ternary.True;
5637-
}
56385596
}
56395597

56405598
/**
@@ -5830,6 +5788,20 @@ namespace ts {
58305788
}
58315789
}
58325790

5791+
// Return true if the given type is the constructor type for an abstract class
5792+
function isAbstractConstructorType(type: Type) {
5793+
if (type.flags & TypeFlags.Anonymous) {
5794+
const symbol = type.symbol;
5795+
if (symbol && symbol.flags & SymbolFlags.Class) {
5796+
const declaration = getClassLikeDeclarationOfSymbol(symbol);
5797+
if (declaration && declaration.flags & NodeFlags.Abstract) {
5798+
return true;
5799+
}
5800+
}
5801+
}
5802+
return false;
5803+
}
5804+
58335805
// Return true if the given type is part of a deeply nested chain of generic instantiations. We consider this to be the case
58345806
// when structural type comparisons have been started for 10 or more instantiations of the same generic type. It is possible,
58355807
// though highly unlikely, for this test to be true in a situation where a chain of instantiations is not infinitely expanding.

0 commit comments

Comments
 (0)