From bf7258742eccd1691c139417715e3eb5e7d18d54 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 12 Jan 2017 10:49:44 -0800 Subject: [PATCH] Improve type relationships for generic mapped types --- src/compiler/checker.ts | 35 +++++++++++++++-------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index cc4afc8aa3e..d8456b8074c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4662,10 +4662,6 @@ namespace ts { return type.modifiersType; } - function getErasedTemplateTypeFromMappedType(type: MappedType) { - return instantiateType(getTemplateTypeFromMappedType(type), createTypeEraser([getTypeParameterFromMappedType(type)])); - } - function isGenericMappedType(type: Type) { if (getObjectFlags(type) & ObjectFlags.Mapped) { const constraintType = getConstraintTypeFromMappedType(type); @@ -7765,25 +7761,24 @@ namespace ts { return result; } - // A type [P in S]: X is related to a type [P in T]: Y if T is related to S and X is related to Y. + // A type [P in S]: X is related to a type [Q in T]: Y if T is related to S and X' is + // related to Y, where X' is an instantiation of X in which P is replaced with Q. Notice + // that S and T are contra-variant whereas X and Y are co-variant. function mappedTypeRelatedTo(source: Type, target: Type, reportErrors: boolean): Ternary { if (isGenericMappedType(target)) { if (isGenericMappedType(source)) { - let result: Ternary; - if (relation === identityRelation) { - const readonlyMatches = !(source).declaration.readonlyToken === !(target).declaration.readonlyToken; - const optionalMatches = !(source).declaration.questionToken === !(target).declaration.questionToken; - if (readonlyMatches && optionalMatches) { - if (result = isRelatedTo(getConstraintTypeFromMappedType(target), getConstraintTypeFromMappedType(source), reportErrors)) { - return result & isRelatedTo(getErasedTemplateTypeFromMappedType(source), getErasedTemplateTypeFromMappedType(target), reportErrors); - } - } - } - else { - if (relation === comparableRelation || !(source).declaration.questionToken || (target).declaration.questionToken) { - if (result = isRelatedTo(getConstraintTypeFromMappedType(target), getConstraintTypeFromMappedType(source), reportErrors)) { - return result & isRelatedTo(getTemplateTypeFromMappedType(source), getTemplateTypeFromMappedType(target), reportErrors); - } + const sourceReadonly = !!(source).declaration.readonlyToken; + const sourceOptional = !!(source).declaration.questionToken; + const targetReadonly = !!(target).declaration.readonlyToken; + const targetOptional = !!(target).declaration.questionToken; + const modifiersRelated = relation === identityRelation ? + sourceReadonly === targetReadonly && sourceOptional === targetOptional : + relation === comparableRelation || !sourceOptional || targetOptional; + if (modifiersRelated) { + let result: Ternary; + if (result = isRelatedTo(getConstraintTypeFromMappedType(target), getConstraintTypeFromMappedType(source), reportErrors)) { + const mapper = createTypeMapper([getTypeParameterFromMappedType(source)], [getTypeParameterFromMappedType(target)]); + return result & isRelatedTo(instantiateType(getTemplateTypeFromMappedType(source), mapper), getTemplateTypeFromMappedType(target), reportErrors); } } }