From 90ce45061822eff333fb351dcbe8f15d1e8ea7ae Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 17 Jul 2024 07:13:49 -0700 Subject: [PATCH] Exclude comparable relation from strict readonly checking --- src/compiler/checker.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0e17f124538..8b8ea3e9540 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -22922,7 +22922,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (getObjectFlags(source) & ObjectFlags.Mapped && !(source as MappedType).declaration.nameType && isRelatedTo(getIndexType(target), getConstraintTypeFromMappedType(source as MappedType), RecursionFlags.Both)) { if ( !(getMappedTypeModifiers(source as MappedType) & MappedTypeModifiers.IncludeOptional) && - !(enforceReadonly && getMappedTypeModifiers(source as MappedType) & MappedTypeModifiers.IncludeReadonly) + !(enforceReadonly && relation !== comparableRelation && getMappedTypeModifiers(source as MappedType) & MappedTypeModifiers.IncludeReadonly) ) { const templateType = getTemplateTypeFromMappedType(source as MappedType); const indexedAccessType = getIndexedAccessType(target, getTypeParameterFromMappedType(source as MappedType)); @@ -23046,7 +23046,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const keysRemapped = !!target.declaration.nameType; const templateType = getTemplateTypeFromMappedType(target); const modifiers = getMappedTypeModifiers(target); - if (!(modifiers & MappedTypeModifiers.ExcludeOptional) && !(enforceReadonly && modifiers & MappedTypeModifiers.ExcludeReadonly)) { + if (!(modifiers & MappedTypeModifiers.ExcludeOptional) && !(enforceReadonly && relation !== comparableRelation && modifiers & MappedTypeModifiers.ExcludeReadonly)) { // If the mapped type has shape `{ [P in Q]: T[P] }`, // source `S` is related to target if `T` = `S`, i.e. `S` is related to `{ [P in Q]: S[P] }`. if ( @@ -23433,7 +23433,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function mappedTypeRelatedTo(source: MappedType, target: MappedType, reportErrors: boolean): Ternary { const modifiersRelated = relation === comparableRelation || (relation === identityRelation ? getMappedTypeModifiers(source) === getMappedTypeModifiers(target) : getMappedTypeModifiersRank(source, MappedTypeModifiers.Optional) <= getMappedTypeModifiersRank(target, MappedTypeModifiers.Optional) && - (!enforceReadonly || getMappedTypeModifiersRank(source, MappedTypeModifiers.Readonly) <= getMappedTypeModifiersRank(target, MappedTypeModifiers.Readonly))); + (!enforceReadonly || relation === comparableRelation || getMappedTypeModifiersRank(source, MappedTypeModifiers.Readonly) <= getMappedTypeModifiersRank(target, MappedTypeModifiers.Readonly))); if (modifiersRelated) { let result: Ternary; const targetConstraint = getConstraintTypeFromMappedType(target); @@ -23605,7 +23605,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // They're still assignable to one another, since `readonly` doesn't affect assignability. // This is only applied during the strictSubtypeRelation -- currently used in subtype reduction if ( - (relation === strictSubtypeRelation || enforceReadonly) && + (relation === strictSubtypeRelation || enforceReadonly && relation !== comparableRelation) && isReadonlySymbol(sourceProp) && !isReadonlySymbol(targetProp) && !(targetProp.flags & SymbolFlags.Method) ) { if (reportErrors) { @@ -24079,7 +24079,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } return Ternary.False; } - if (enforceReadonly && sourceInfo.isReadonly && !targetInfo.isReadonly) { + if (enforceReadonly && relation !== comparableRelation && sourceInfo.isReadonly && !targetInfo.isReadonly) { if (reportErrors) { reportError(Diagnostics._0_index_signature_is_readonly_in_the_source_but_not_in_the_target, typeToString(sourceInfo.keyType)); }