From 47e6aef85832e36b19eb188ae404a8e3f4c94205 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 24 Jul 2017 13:59:43 -0700 Subject: [PATCH] Given T extends Foo, make Partial related to Partial --- src/compiler/checker.ts | 59 +++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 34 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c2f40c169dc..4b824e8825d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5765,12 +5765,13 @@ namespace ts { return type.modifiersType; } + function isPartialMappedType(type: Type) { + return getObjectFlags(type) & ObjectFlags.Mapped && !!(type).declaration.questionToken; + } + function isGenericMappedType(type: Type) { - if (getObjectFlags(type) & ObjectFlags.Mapped) { - const constraintType = getConstraintTypeFromMappedType(type); - return maybeTypeOfKind(constraintType, TypeFlags.TypeVariable | TypeFlags.Index); - } - return false; + return getObjectFlags(type) & ObjectFlags.Mapped && + maybeTypeOfKind(getConstraintTypeFromMappedType(type), TypeFlags.TypeVariable | TypeFlags.Index); } function resolveStructuredTypeMembers(type: StructuredType): ResolvedType { @@ -9254,8 +9255,12 @@ namespace ts { if (source.flags & (TypeFlags.Object | TypeFlags.Intersection) && target.flags & TypeFlags.Object) { // Report structural errors only if we haven't reported any errors yet const reportStructuralErrors = reportErrors && errorInfo === saveErrorInfo && !sourceIsPrimitive; - if (isGenericMappedType(source) || isGenericMappedType(target)) { - result = mappedTypeRelatedTo(source, target, reportStructuralErrors); + // An empty object type is related to any mapped type that includes a '?' modifier. + if (isPartialMappedType(target) && !isGenericMappedType(source) && isEmptyObjectType(source)) { + result = Ternary.True; + } + else if (isGenericMappedType(target)) { + result = isGenericMappedType(source) ? mappedTypeRelatedTo(source, target, reportStructuralErrors) : Ternary.False; } else { result = propertiesRelatedTo(source, target, reportStructuralErrors); @@ -9284,33 +9289,19 @@ namespace ts { // 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)) { - 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); - } - } - } - else if ((target).declaration.questionToken && isEmptyObjectType(source)) { - return Ternary.True; - - } - } - else if (relation !== identityRelation) { - const resolved = resolveStructuredTypeMembers(target); - if (isEmptyResolvedType(resolved) || resolved.stringIndexInfo && resolved.stringIndexInfo.type.flags & TypeFlags.Any) { - return Ternary.True; + function mappedTypeRelatedTo(source: MappedType, target: MappedType, reportErrors: boolean): Ternary { + 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); } } return Ternary.False;