mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-15 03:23:08 -06:00
Merge pull request #6111 from Microsoft/simplifyAbstractCheck
Simplify abstract constructor type assignability checking
This commit is contained in:
commit
66a0f1d2ea
@ -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.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user