More exploration of indexed access type constraints in type relations

This commit is contained in:
Anders Hejlsberg
2019-04-02 10:02:09 -07:00
parent 3e09d29b95
commit d61a6935d3
+30 -25
View File
@@ -7488,15 +7488,18 @@ namespace ts {
}
function getConstraintFromIndexedAccess(type: IndexedAccessType) {
const objectType = getConstraintOfType(type.objectType) || type.objectType;
if (objectType !== type.objectType) {
const constraint = getIndexedAccessTypeOrUndefined(objectType, type.indexType);
if (constraint) {
return constraint;
const indexConstraint = getConstraintOfType(type.indexType);
if (indexConstraint && indexConstraint !== type.indexType) {
const indexedAccess = getIndexedAccessTypeOrUndefined(type.objectType, indexConstraint);
if (indexedAccess) {
return indexedAccess;
}
}
const baseConstraint = getBaseConstraintOfType(type);
return baseConstraint && baseConstraint !== type ? baseConstraint : undefined;
const objectConstraint = getConstraintOfType(type.objectType);
if (objectConstraint && objectConstraint !== type.objectType) {
return getIndexedAccessTypeOrUndefined(objectConstraint, type.indexType);
}
return undefined;
}
function getDefaultConstraintOfConditionalType(type: ConditionalType) {
@@ -9920,14 +9923,14 @@ namespace ts {
if (objectType.flags & (TypeFlags.Any | TypeFlags.Never)) {
return objectType;
}
const isAssignment = accessExpression && (isAssignmentTarget(accessExpression) || isDeleteTarget(accessExpression));
if (isAssignment && maybeTypeOfKind(originalObjectType, TypeFlags.Instantiable)) {
error(accessExpression, Diagnostics.Type_0_cannot_be_indexed_by_type_1, typeToString(originalObjectType), typeToString(indexType));
return undefined;
}
const indexInfo = isTypeAssignableToKind(indexType, TypeFlags.NumberLike) && getIndexInfoOfType(objectType, IndexKind.Number) ||
getIndexInfoOfType(objectType, IndexKind.String);
if (indexInfo) {
const isAssignment = accessExpression && (isAssignmentTarget(accessExpression) || isDeleteTarget(accessExpression));
if (isAssignment && maybeTypeOfKind(originalObjectType, TypeFlags.Instantiable)) {
error(accessExpression, Diagnostics.Type_0_cannot_be_indexed_by_type_1, typeToString(originalObjectType), typeToString(indexType));
return undefined;
}
if (accessNode && !isTypeAssignableToKind(indexType, TypeFlags.String | TypeFlags.Number)) {
const indexNode = getIndexNodeForAccessExpression(accessNode);
error(indexNode, Diagnostics.Type_0_cannot_be_used_as_an_index_type, typeToString(indexType));
@@ -12820,7 +12823,7 @@ namespace ts {
// A type S is related to a type T[K], where T and K aren't both type variables, if S is related to C,
// where C is the base constraint of T[K]
if (relation !== identityRelation) {
const objectType = (<IndexedAccessType>target).objectType
const objectType = (<IndexedAccessType>target).objectType;
const indexType = (<IndexedAccessType>target).indexType;
if (indexType.flags & TypeFlags.StructuredOrInstantiable) {
const keyType = getLowerBoundOfKeyType(indexType, /*isIndexType*/ true);
@@ -12885,23 +12888,25 @@ namespace ts {
return result;
}
}
const constraint = getConstraintOfType(<TypeVariable>source);
if (!constraint || (source.flags & 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))) {
else {
const constraint = getConstraintOfType(<TypeVariable>source);
if (!constraint || (source.flags & 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))) {
errorInfo = saveErrorInfo;
return result;
}
}
// 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, /*reportErrors*/ false, /*headMessage*/ undefined, isIntersectionConstituent)) {
errorInfo = saveErrorInfo;
return result;
}
}
// 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, /*reportErrors*/ false, /*headMessage*/ undefined, isIntersectionConstituent)) {
// 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, reportErrors, /*headMessage*/ undefined, isIntersectionConstituent)) {
errorInfo = 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, reportErrors, /*headMessage*/ undefined, isIntersectionConstituent)) {
errorInfo = saveErrorInfo;
return result;
}
}
}
else if (source.flags & TypeFlags.Index) {