diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1fdac127c502b..1a1a01d6dd047 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5528,32 +5528,24 @@ namespace ts { if (target === anyFunctionType || source === anyFunctionType) { return Ternary.True; } + const sourceSignatures = getSignaturesOfType(source, kind); const targetSignatures = getSignaturesOfType(target, kind); - 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; + 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; + outer: for (const t of targetSignatures) { if (!t.hasStringLiterals || target.flags & TypeFlags.FromSignature) { // Only elaborate errors from the first failure @@ -5580,40 +5572,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; - } } function signatureRelatedTo(source: Signature, target: Signature, reportErrors: boolean): Ternary { @@ -5782,6 +5740,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. @@ -15396,20 +15368,6 @@ namespace ts { return symbol && getExportSymbolOfValueSymbolIfExported(symbol).valueDeclaration; } - function instantiateSingleCallFunctionType(functionType: Type, typeArguments: Type[]): Type { - if (functionType === unknownType) { - return unknownType; - } - - const signature = getSingleCallSignature(functionType); - if (!signature) { - return unknownType; - } - - const instantiatedSignature = getSignatureInstantiation(signature, typeArguments); - return getOrCreateTypeFromSignature(instantiatedSignature); - } - function createResolver(): EmitResolver { return { getReferencedExportContainer,