diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4a770790384..02c5a57b7fe 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -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 = (target).objectType + const objectType = (target).objectType; const indexType = (target).indexType; if (indexType.flags & TypeFlags.StructuredOrInstantiable) { const keyType = getLowerBoundOfKeyType(indexType, /*isIndexType*/ true); @@ -12885,23 +12888,25 @@ namespace ts { return result; } } - const constraint = getConstraintOfType(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(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) {