Fix type parameter comparability to consistently allow comparisons on unconstrained type parameters (#48861)

* Fix type parameter comparability to consistently allow comparisons on unconstrained type parameters

* Less elaboration, non-strict-mode fix
This commit is contained in:
Wesley Wigham
2022-05-09 12:22:00 -07:00
committed by GitHub
parent 1071240907
commit f84ec3e8b4
10 changed files with 291 additions and 351 deletions

View File

@@ -19347,6 +19347,20 @@ namespace ts {
}
}
}
if (relation === comparableRelation && sourceFlags & TypeFlags.TypeParameter) {
// This is a carve-out in comparability to essentially forbid comparing a type parameter
// with another type parameter unless one extends the other. (Remember: comparability is mostly bidirectional!)
let constraint = getConstraintOfTypeParameter(source);
if (constraint && hasNonCircularBaseConstraint(source)) {
while (constraint && constraint.flags & TypeFlags.TypeParameter) {
if (result = isRelatedTo(constraint, target, RecursionFlags.Source, /*reportErrors*/ false)) {
return result;
}
constraint = getConstraintOfTypeParameter(constraint);
}
}
return Ternary.False;
}
}
else if (targetFlags & TypeFlags.Index) {
const targetType = (target as IndexType).type;
@@ -19558,8 +19572,8 @@ namespace ts {
if (sourceFlags & TypeFlags.TypeVariable) {
// IndexedAccess comparisons are handled above in the `targetFlags & TypeFlage.IndexedAccess` branch
if (!(sourceFlags & TypeFlags.IndexedAccess && targetFlags & TypeFlags.IndexedAccess)) {
const constraint = getConstraintOfType(source as TypeVariable);
if (!constraint || (sourceFlags & TypeFlags.TypeParameter && constraint.flags & TypeFlags.Any)) {
const constraint = getConstraintOfType(source as TypeVariable) || unknownType;
if (!getConstraintOfType(source as TypeVariable) || (sourceFlags & TypeFlags.TypeParameter && constraint.flags & TypeFlags.Any)) {
// A type variable with no constraint is not related to the non-primitive object type.
if (result = isRelatedTo(emptyObjectType, extractTypesOfKind(target, ~TypeFlags.NonPrimitive), RecursionFlags.Both)) {
resetErrorInfo(saveErrorInfo);
@@ -19567,12 +19581,12 @@ namespace ts {
}
}
// hi-speed no-this-instantiation check (less accurate, but avoids costly `this`-instantiation when the constraint will suffice), see #28231 for report on why this is needed
else if (result = isRelatedTo(constraint, target, RecursionFlags.Source, /*reportErrors*/ false, /*headMessage*/ undefined, intersectionState)) {
if (result = isRelatedTo(constraint, target, RecursionFlags.Source, /*reportErrors*/ false, /*headMessage*/ undefined, intersectionState)) {
resetErrorInfo(saveErrorInfo);
return result;
}
// slower, fuller, this-instantiated check (necessary when comparing raw `this` types from base classes), see `subclassWithPolymorphicThisIsAssignable.ts` test for example
else if (result = isRelatedTo(getTypeWithThisArgument(constraint, source), target, RecursionFlags.Source, reportErrors && !(targetFlags & sourceFlags & TypeFlags.TypeParameter), /*headMessage*/ undefined, intersectionState)) {
else if (result = isRelatedTo(getTypeWithThisArgument(constraint, source), target, RecursionFlags.Source, reportErrors && constraint !== unknownType && !(targetFlags & sourceFlags & TypeFlags.TypeParameter), /*headMessage*/ undefined, intersectionState)) {
resetErrorInfo(saveErrorInfo);
return result;
}