Skip to content

Commit fd52e9a

Browse files
committed
Simplify abstract constructor type assignability checking
1 parent 55d4f0f commit fd52e9a

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
@@ -5528,32 +5528,24 @@ namespace ts {
55285528
if (target === anyFunctionType || source === anyFunctionType) {
55295529
return Ternary.True;
55305530
}
5531+
55315532
const sourceSignatures = getSignaturesOfType(source, kind);
55325533
const targetSignatures = getSignaturesOfType(target, kind);
5533-
let result = Ternary.True;
5534-
const saveErrorInfo = errorInfo;
5535-
5536-
5537-
5538-
if (kind === SignatureKind.Construct) {
5539-
// Only want to compare the construct signatures for abstractness guarantees.
5540-
5541-
// Because the "abstractness" of a class is the same across all construct signatures
5542-
// (internally we are checking the corresponding declaration), it is enough to perform
5543-
// the check and report an error once over all pairs of source and target construct signatures.
5544-
//
5545-
// sourceSig and targetSig are (possibly) undefined.
5546-
//
5547-
// Note that in an extends-clause, targetSignatures is stripped, so the check never proceeds.
5548-
const sourceSig = sourceSignatures[0];
5549-
const targetSig = targetSignatures[0];
5550-
5551-
result &= abstractSignatureRelatedTo(source, sourceSig, target, targetSig);
5552-
if (result !== Ternary.True) {
5553-
return result;
5534+
if (kind === SignatureKind.Construct && sourceSignatures.length && targetSignatures.length &&
5535+
isAbstractConstructorType(source) && !isAbstractConstructorType(target)) {
5536+
// An abstract constructor type is not assignable to a non-abstract constructor type
5537+
// as it would otherwise be possible to new an abstract class. Note that the assignablity
5538+
// check we perform for an extends clause excludes construct signatures from the target,
5539+
// so this check never proceeds.
5540+
if (reportErrors) {
5541+
reportError(Diagnostics.Cannot_assign_an_abstract_constructor_type_to_a_non_abstract_constructor_type);
55545542
}
5543+
return Ternary.False;
55555544
}
55565545

5546+
let result = Ternary.True;
5547+
const saveErrorInfo = errorInfo;
5548+
55575549
outer: for (const t of targetSignatures) {
55585550
if (!t.hasStringLiterals || target.flags & TypeFlags.FromSignature) {
55595551
// Only elaborate errors from the first failure
@@ -5580,40 +5572,6 @@ namespace ts {
55805572
}
55815573
}
55825574
return result;
5583-
5584-
function abstractSignatureRelatedTo(source: Type, sourceSig: Signature, target: Type, targetSig: Signature) {
5585-
if (sourceSig && targetSig) {
5586-
5587-
const sourceDecl = source.symbol && getClassLikeDeclarationOfSymbol(source.symbol);
5588-
const targetDecl = target.symbol && getClassLikeDeclarationOfSymbol(target.symbol);
5589-
5590-
if (!sourceDecl) {
5591-
// If the source object isn't itself a class declaration, it can be freely assigned, regardless
5592-
// of whether the constructed object is abstract or not.
5593-
return Ternary.True;
5594-
}
5595-
5596-
const sourceErasedSignature = getErasedSignature(sourceSig);
5597-
const targetErasedSignature = getErasedSignature(targetSig);
5598-
5599-
const sourceReturnType = sourceErasedSignature && getReturnTypeOfSignature(sourceErasedSignature);
5600-
const targetReturnType = targetErasedSignature && getReturnTypeOfSignature(targetErasedSignature);
5601-
5602-
const sourceReturnDecl = sourceReturnType && sourceReturnType.symbol && getClassLikeDeclarationOfSymbol(sourceReturnType.symbol);
5603-
const targetReturnDecl = targetReturnType && targetReturnType.symbol && getClassLikeDeclarationOfSymbol(targetReturnType.symbol);
5604-
const sourceIsAbstract = sourceReturnDecl && sourceReturnDecl.flags & NodeFlags.Abstract;
5605-
const targetIsAbstract = targetReturnDecl && targetReturnDecl.flags & NodeFlags.Abstract;
5606-
5607-
if (sourceIsAbstract && !(targetIsAbstract && targetDecl)) {
5608-
// if target isn't a class-declaration type, then it can be new'd, so we forbid the assignment.
5609-
if (reportErrors) {
5610-
reportError(Diagnostics.Cannot_assign_an_abstract_constructor_type_to_a_non_abstract_constructor_type);
5611-
}
5612-
return Ternary.False;
5613-
}
5614-
}
5615-
return Ternary.True;
5616-
}
56175575
}
56185576

56195577
function signatureRelatedTo(source: Signature, target: Signature, reportErrors: boolean): Ternary {
@@ -5782,6 +5740,20 @@ namespace ts {
57825740
}
57835741
}
57845742

5743+
// Return true if the given type is the constructor type for an abstract class
5744+
function isAbstractConstructorType(type: Type) {
5745+
if (type.flags & TypeFlags.Anonymous) {
5746+
const symbol = type.symbol;
5747+
if (symbol && symbol.flags & SymbolFlags.Class) {
5748+
const declaration = getClassLikeDeclarationOfSymbol(symbol);
5749+
if (declaration && declaration.flags & NodeFlags.Abstract) {
5750+
return true;
5751+
}
5752+
}
5753+
}
5754+
return false;
5755+
}
5756+
57855757
// Return true if the given type is part of a deeply nested chain of generic instantiations. We consider this to be the case
57865758
// when structural type comparisons have been started for 10 or more instantiations of the same generic type. It is possible,
57875759
// 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)