Merge pull request #17382 from Microsoft/fixPartialTypeRelations

Fix partial type relations
This commit is contained in:
Anders Hejlsberg
2017-07-27 17:31:55 -07:00
committed by GitHub
8 changed files with 203 additions and 65 deletions

View File

@@ -5787,12 +5787,13 @@ namespace ts {
return type.modifiersType;
}
function isPartialMappedType(type: Type) {
return getObjectFlags(type) & ObjectFlags.Mapped && !!(<MappedType>type).declaration.questionToken;
}
function isGenericMappedType(type: Type) {
if (getObjectFlags(type) & ObjectFlags.Mapped) {
const constraintType = getConstraintTypeFromMappedType(<MappedType>type);
return maybeTypeOfKind(constraintType, TypeFlags.TypeVariable | TypeFlags.Index);
}
return false;
return getObjectFlags(type) & ObjectFlags.Mapped &&
maybeTypeOfKind(getConstraintTypeFromMappedType(<MappedType>type), TypeFlags.TypeVariable | TypeFlags.Index);
}
function resolveStructuredTypeMembers(type: StructuredType): ResolvedType {
@@ -9280,8 +9281,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(<MappedType>source, <MappedType>target, reportStructuralErrors) : Ternary.False;
}
else {
result = propertiesRelatedTo(source, target, reportStructuralErrors);
@@ -9310,33 +9315,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 = !!(<MappedType>source).declaration.readonlyToken;
const sourceOptional = !!(<MappedType>source).declaration.questionToken;
const targetReadonly = !!(<MappedType>target).declaration.readonlyToken;
const targetOptional = !!(<MappedType>target).declaration.questionToken;
const modifiersRelated = relation === identityRelation ?
sourceReadonly === targetReadonly && sourceOptional === targetOptional :
relation === comparableRelation || !sourceOptional || targetOptional;
if (modifiersRelated) {
let result: Ternary;
if (result = isRelatedTo(getConstraintTypeFromMappedType(<MappedType>target), getConstraintTypeFromMappedType(<MappedType>source), reportErrors)) {
const mapper = createTypeMapper([getTypeParameterFromMappedType(<MappedType>source)], [getTypeParameterFromMappedType(<MappedType>target)]);
return result & isRelatedTo(instantiateType(getTemplateTypeFromMappedType(<MappedType>source), mapper), getTemplateTypeFromMappedType(<MappedType>target), reportErrors);
}
}
}
else if ((<MappedType>target).declaration.questionToken && isEmptyObjectType(source)) {
return Ternary.True;
}
}
else if (relation !== identityRelation) {
const resolved = resolveStructuredTypeMembers(<ObjectType>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(<MappedType>target), getConstraintTypeFromMappedType(<MappedType>source), reportErrors)) {
const mapper = createTypeMapper([getTypeParameterFromMappedType(<MappedType>source)], [getTypeParameterFromMappedType(<MappedType>target)]);
return result & isRelatedTo(instantiateType(getTemplateTypeFromMappedType(<MappedType>source), mapper), getTemplateTypeFromMappedType(<MappedType>target), reportErrors);
}
}
return Ternary.False;