@@ -4668,19 +4668,21 @@ namespace ts {
46684668 let result: Ternary;
46694669 // both types are the same - covers 'they are the same primitive type or both are Any' or the same type parameter cases
46704670 if (source === target) return Ternary.True;
4671- if (relation !== identityRelation) {
4672- if (isTypeAny(target)) return Ternary.True;
4673- if (source === undefinedType) return Ternary.True;
4674- if (source === nullType && target !== undefinedType) return Ternary.True;
4675- if (source.flags & TypeFlags.Enum && target === numberType) return Ternary.True;
4676- if (source.flags & TypeFlags.StringLiteral && target === stringType) return Ternary.True;
4677- if (relation === assignableRelation) {
4678- if (isTypeAny(source)) return Ternary.True;
4679- if (source === numberType && target.flags & TypeFlags.Enum) return Ternary.True;
4680- }
4671+ if (relation === identityRelation) {
4672+ return isIdenticalTo(source, target);
4673+ }
4674+
4675+ if (isTypeAny(target)) return Ternary.True;
4676+ if (source === undefinedType) return Ternary.True;
4677+ if (source === nullType && target !== undefinedType) return Ternary.True;
4678+ if (source.flags & TypeFlags.Enum && target === numberType) return Ternary.True;
4679+ if (source.flags & TypeFlags.StringLiteral && target === stringType) return Ternary.True;
4680+ if (relation === assignableRelation) {
4681+ if (isTypeAny(source)) return Ternary.True;
4682+ if (source === numberType && target.flags & TypeFlags.Enum) return Ternary.True;
46814683 }
46824684
4683- if (relation !== identityRelation && source.flags & TypeFlags.FreshObjectLiteral) {
4685+ if (source.flags & TypeFlags.FreshObjectLiteral) {
46844686 if (hasExcessProperties(<FreshObjectLiteralType>source, target, reportErrors)) {
46854687 if (reportErrors) {
46864688 reportRelationError(headMessage, source, target);
@@ -4696,78 +4698,66 @@ namespace ts {
46964698
46974699 let saveErrorInfo = errorInfo;
46984700
4699- if (source.flags & TypeFlags.Reference && target.flags & TypeFlags.Reference && (<TypeReference>source).target === (<TypeReference>target).target) {
4700- // We have type references to same target type, see if relationship holds for all type arguments
4701- if (result = typesRelatedTo((<TypeReference >source).typeArguments, (<TypeReference> target).typeArguments , reportErrors)) {
4701+ // Note that the "each" checks must precede the "some" checks to produce the correct results
4702+ if (source.flags & TypeFlags.Union) {
4703+ if (result = eachTypeRelatedToType(<UnionType >source, target, reportErrors)) {
47024704 return result;
47034705 }
47044706 }
4705- else if (source.flags & TypeFlags.TypeParameter && target.flags & TypeFlags.TypeParameter ) {
4706- if (result = typeParameterRelatedTo(<TypeParameter> source, <TypeParameter >target, reportErrors)) {
4707+ else if (target.flags & TypeFlags.Intersection ) {
4708+ if (result = typeRelatedToEachType( source, <IntersectionType >target, reportErrors)) {
47074709 return result;
47084710 }
47094711 }
4710- else if (relation !== identityRelation) {
4711- // Note that the "each" checks must precede the "some" checks to produce the correct results
4712- if (source.flags & TypeFlags.Union) {
4713- if (result = eachTypeRelatedToType(<UnionType>source, target, reportErrors)) {
4712+ else {
4713+ // It is necessary to try "some" checks on both sides because there may be nested "each" checks
4714+ // on either side that need to be prioritized. For example, A | B = (A | B) & (C | D) or
4715+ // A & B = (A & B) | (C & D).
4716+ if (source.flags & TypeFlags.Intersection) {
4717+ // If target is a union type the following check will report errors so we suppress them here
4718+ if (result = someTypeRelatedToType(<IntersectionType>source, target, reportErrors && !(target.flags & TypeFlags.Union))) {
47144719 return result;
47154720 }
47164721 }
4717- else if (target.flags & TypeFlags.Intersection ) {
4718- if (result = typeRelatedToEachType (source, <IntersectionType >target, reportErrors)) {
4722+ if (target.flags & TypeFlags.Union ) {
4723+ if (result = typeRelatedToSomeType (source, <UnionType >target, reportErrors)) {
47194724 return result;
47204725 }
47214726 }
4722- else {
4723- // It is necessary to try "each" checks on both sides because there may be nested "some" checks
4724- // on either side that need to be prioritized. For example, A | B = (A | B) & (C | D) or
4725- // A & B = (A & B) | (C & D).
4726- if (source.flags & TypeFlags.Intersection) {
4727- // If target is a union type the following check will report errors so we suppress them here
4728- if (result = someTypeRelatedToType(<IntersectionType>source, target, reportErrors && !(target.flags & TypeFlags.Union))) {
4729- return result;
4730- }
4731- }
4732- if (target.flags & TypeFlags.Union) {
4733- if (result = typeRelatedToSomeType(source, <UnionType>target, reportErrors)) {
4734- return result;
4735- }
4736- }
4737- }
4738- }
4739- else {
4740- if (source.flags & TypeFlags.Union && target.flags & TypeFlags.Union ||
4741- source.flags & TypeFlags.Intersection && target.flags & TypeFlags.Intersection) {
4742- if (result = eachTypeRelatedToSomeType(<UnionOrIntersectionType>source, <UnionOrIntersectionType>target)) {
4743- if (result &= eachTypeRelatedToSomeType(<UnionOrIntersectionType>target, <UnionOrIntersectionType>source)) {
4744- return result;
4745- }
4746- }
4747- }
47484727 }
47494728
4750- // Even if relationship doesn't hold for unions, type parameters, or generic type references,
4751- // it may hold in a structural comparison.
4752- // Report structural errors only if we haven't reported any errors yet
4753- let reportStructuralErrors = reportErrors && errorInfo === saveErrorInfo;
4754- // Identity relation does not use apparent type
4755- let sourceOrApparentType = relation === identityRelation ? source : getApparentType(source);
4756- // In a check of the form X = A & B, we will have previously checked if A relates to X or B relates
4757- // to X. Failing both of those we want to check if the aggregation of A and B's members structurally
4758- // relates to X. Thus, we include intersection types on the source side here.
4759- if (sourceOrApparentType.flags & (TypeFlags.ObjectType | TypeFlags.Intersection) && target.flags & TypeFlags.ObjectType) {
4760- if (result = objectTypeRelatedTo(sourceOrApparentType, <ObjectType>target, reportStructuralErrors)) {
4729+ if (source.flags & TypeFlags.TypeParameter) {
4730+ let constraint = getConstraintOfTypeParameter(<TypeParameter>source);
4731+ if (!constraint || constraint.flags & TypeFlags.Any) {
4732+ constraint = emptyObjectType;
4733+ }
4734+ // Report constraint errors only if the constraint is not the empty object type
4735+ let reportConstraintErrors = reportErrors && constraint !== emptyObjectType;
4736+ if (result = isRelatedTo(constraint, target, reportConstraintErrors)) {
47614737 errorInfo = saveErrorInfo;
47624738 return result;
47634739 }
47644740 }
4765- else if (source.flags & TypeFlags.TypeParameter && sourceOrApparentType.flags & TypeFlags.UnionOrIntersection) {
4766- // We clear the errors first because the following check often gives a better error than
4767- // the union or intersection comparison above if it is applicable.
4768- errorInfo = saveErrorInfo;
4769- if (result = isRelatedTo(sourceOrApparentType, target, reportErrors)) {
4770- return result;
4741+ else {
4742+ if (source.flags & TypeFlags.Reference && target.flags & TypeFlags.Reference && (<TypeReference>source).target === (<TypeReference>target).target) {
4743+ // We have type references to same target type, see if relationship holds for all type arguments
4744+ if (result = typesRelatedTo((<TypeReference>source).typeArguments, (<TypeReference>target).typeArguments, reportErrors)) {
4745+ return result;
4746+ }
4747+ }
4748+ // Even if relationship doesn't hold for unions, intersections, or generic type references,
4749+ // it may hold in a structural comparison.
4750+ let apparentType = getApparentType(source);
4751+ // In a check of the form X = A & B, we will have previously checked if A relates to X or B relates
4752+ // to X. Failing both of those we want to check if the aggregation of A and B's members structurally
4753+ // relates to X. Thus, we include intersection types on the source side here.
4754+ if (apparentType.flags & (TypeFlags.ObjectType | TypeFlags.Intersection) && target.flags & TypeFlags.ObjectType) {
4755+ // Report structural errors only if we haven't reported any errors yet
4756+ let reportStructuralErrors = reportErrors && errorInfo === saveErrorInfo;
4757+ if (result = objectTypeRelatedTo(apparentType, <ObjectType>target, reportStructuralErrors)) {
4758+ errorInfo = saveErrorInfo;
4759+ return result;
4760+ }
47714761 }
47724762 }
47734763
@@ -4777,6 +4767,31 @@ namespace ts {
47774767 return Ternary.False;
47784768 }
47794769
4770+ function isIdenticalTo(source: Type, target: Type): Ternary {
4771+ let result: Ternary;
4772+ if (source.flags & TypeFlags.ObjectType && target.flags & TypeFlags.ObjectType) {
4773+ if (source.flags & TypeFlags.Reference && target.flags & TypeFlags.Reference && (<TypeReference>source).target === (<TypeReference>target).target) {
4774+ // We have type references to same target type, see if all type arguments are identical
4775+ if (result = typesRelatedTo((<TypeReference>source).typeArguments, (<TypeReference>target).typeArguments, /*reportErrors*/ false)) {
4776+ return result;
4777+ }
4778+ }
4779+ return objectTypeRelatedTo(<ObjectType>source, <ObjectType>target, /*reportErrors*/ false);
4780+ }
4781+ if (source.flags & TypeFlags.TypeParameter && target.flags & TypeFlags.TypeParameter) {
4782+ return typeParameterIdenticalTo(<TypeParameter>source, <TypeParameter>target);
4783+ }
4784+ if (source.flags & TypeFlags.Union && target.flags & TypeFlags.Union ||
4785+ source.flags & TypeFlags.Intersection && target.flags & TypeFlags.Intersection) {
4786+ if (result = eachTypeRelatedToSomeType(<UnionOrIntersectionType>source, <UnionOrIntersectionType>target)) {
4787+ if (result &= eachTypeRelatedToSomeType(<UnionOrIntersectionType>target, <UnionOrIntersectionType>source)) {
4788+ return result;
4789+ }
4790+ }
4791+ }
4792+ return Ternary.False;
4793+ }
4794+
47804795 function hasExcessProperties(source: FreshObjectLiteralType, target: Type, reportErrors: boolean): boolean {
47814796 for (let prop of getPropertiesOfObjectType(source)) {
47824797 if (!isKnownProperty(target, prop.name)) {
@@ -4861,29 +4876,18 @@ namespace ts {
48614876 return result;
48624877 }
48634878
4864- function typeParameterRelatedTo(source: TypeParameter, target: TypeParameter, reportErrors: boolean): Ternary {
4865- if (relation === identityRelation) {
4866- if (source.symbol.name !== target.symbol.name) {
4867- return Ternary.False;
4868- }
4869- // covers case when both type parameters does not have constraint (both equal to noConstraintType)
4870- if (source.constraint === target.constraint) {
4871- return Ternary.True;
4872- }
4873- if (source.constraint === noConstraintType || target.constraint === noConstraintType) {
4874- return Ternary.False;
4875- }
4876- return isRelatedTo(source.constraint, target.constraint, reportErrors);
4879+ function typeParameterIdenticalTo(source: TypeParameter, target: TypeParameter): Ternary {
4880+ if (source.symbol.name !== target.symbol.name) {
4881+ return Ternary.False;
48774882 }
4878- else {
4879- while (true) {
4880- let constraint = getConstraintOfTypeParameter(source);
4881- if (constraint === target) return Ternary.True;
4882- if (!(constraint && constraint.flags & TypeFlags.TypeParameter)) break;
4883- source = <TypeParameter>constraint;
4884- }
4883+ // covers case when both type parameters does not have constraint (both equal to noConstraintType)
4884+ if (source.constraint === target.constraint) {
4885+ return Ternary.True;
4886+ }
4887+ if (source.constraint === noConstraintType || target.constraint === noConstraintType) {
48854888 return Ternary.False;
48864889 }
4890+ return isIdenticalTo(source.constraint, target.constraint);
48874891 }
48884892
48894893 // Determine if two object types are related by structure. First, check if the result is already available in the global cache.
0 commit comments