docs: function checkTypeRelatedTo(source, target, relation, errorNode, headMessage, containingMessageChain) {
              var errorInfo;
              var sourceStack;
              var targetStack;
              var maybeStack;
              var expandingFlags;
              var depth = 0;
              var overflow = false;
              ts.Debug.assert(relation !== identityRelation || !errorNode, "no error reporting in identity checking");
              var result = isRelatedTo(source, target, /*reportErrors*/ !!errorNode, headMessage);
              if (overflow) {
                  error(errorNode, ts.Diagnostics.Excessive_stack_depth_comparing_types_0_and_1, typeToString(source), typeToString(target));
              }
              else if (errorInfo) {
                  if (containingMessageChain) {
                      errorInfo = ts.concatenateDiagnosticMessageChains(containingMessageChain, errorInfo);
                  }
                  diagnostics.add(ts.createDiagnosticForNodeFromMessageChain(errorNode, errorInfo));
              }
              return result !== 0 /* False */;
              function reportError(message, arg0, arg1, arg2) {
                  ts.Debug.assert(!!errorNode);
                  errorInfo = ts.chainDiagnosticMessages(errorInfo, message, arg0, arg1, arg2);
              }
              function reportRelationError(message, source, target) {
                  var sourceType = typeToString(source);
                  var targetType = typeToString(target);
                  if (sourceType === targetType) {
                      sourceType = typeToString(source, /*enclosingDeclaration*/ undefined, 128 /* UseFullyQualifiedType */);
                      targetType = typeToString(target, /*enclosingDeclaration*/ undefined, 128 /* UseFullyQualifiedType */);
                  }
                  if (!message) {
                      message = relation === comparableRelation ?
                          ts.Diagnostics.Type_0_is_not_comparable_to_type_1 :
                          ts.Diagnostics.Type_0_is_not_assignable_to_type_1;
                  }
                  reportError(message, sourceType, targetType);
              }
              function tryElaborateErrorsForPrimitivesAndObjects(source, target) {
                  var sourceType = typeToString(source);
                  var targetType = typeToString(target);
                  if ((globalStringType === source && stringType === target) ||
                      (globalNumberType === source && numberType === target) ||
                      (globalBooleanType === source && booleanType === target) ||
                      (getGlobalESSymbolType() === source && esSymbolType === target)) {
                      reportError(ts.Diagnostics._0_is_a_primitive_but_1_is_a_wrapper_object_Prefer_using_0_when_possible, targetType, sourceType);
                  }
              }
              // Compare two types and return
              // Ternary.True if they are related with no assumptions,
              // Ternary.Maybe if they are related with assumptions of other relationships, or
              // Ternary.False if they are not related.
              function isRelatedTo(source, target, reportErrors, headMessage) {
                  var result;
                  // both types are the same - covers 'they are the same primitive type or both are Any' or the same type parameter cases
                  if (source === target)
                      return -1 /* True */;
                  if (relation === identityRelation) {
                      return isIdenticalTo(source, target);
                  }
                  if (isSimpleTypeRelatedTo(source, target, relation, reportErrors ? reportError : undefined))
                      return -1 /* True */;
                  if (source.flags & 16777216 /* FreshObjectLiteral */) {
                      if (hasExcessProperties(source, target, reportErrors)) {
                          if (reportErrors) {
                              reportRelationError(headMessage, source, target);
                          }
                          return 0 /* False */;
                      }
                      // Above we check for excess properties with respect to the entire target type. When union
                      // and intersection types are further deconstructed on the target side, we don't want to
                      // make the check again (as it might fail for a partial target type). Therefore we obtain
                      // the regular source type and proceed with that.
                      if (target.flags & 1572864 /* UnionOrIntersection */) {
                          source = getRegularTypeOfObjectLiteral(source);
                      }
                  }
                  var saveErrorInfo = errorInfo;
                  // Note that these checks are specifically ordered to produce correct results.
                  if (source.flags & 524288 /* Union */) {
                      if (relation === comparableRelation) {
                          result = someTypeRelatedToType(source, target, reportErrors && !(source.flags & 8190 /* Primitive */));
                      }
                      else {
                          result = eachTypeRelatedToType(source, target, reportErrors && !(source.flags & 8190 /* Primitive */));
                      }
                      if (result) {
                          return result;
                      }
                  }
                  else if (target.flags & 1048576 /* Intersection */) {
                      result = typeRelatedToEachType(source, target, reportErrors);
                      if (result) {
                          return result;
                      }
                  }
                  else {
                      // It is necessary to try these "some" checks on both sides because there may be nested "each" checks
                      // on either side that need to be prioritized. For example, A | B = (A | B) & (C | D) or
                      // A & B = (A & B) | (C & D).
                      if (source.flags & 1048576 /* Intersection */) {
                          // Check to see if any constituents of the intersection are immediately related to the target.
                          //
                          // Don't report errors though. Checking whether a constituent is related to the source is not actually
                          // useful and leads to some confusing error messages. Instead it is better to let the below checks
                          // take care of this, or to not elaborate at all. For instance,
                          //
                          //    - For an object type (such as 'C = A & B'), users are usually more interested in structural errors.
                          //
                          //    - For a union type (such as '(A | B) = (C & D)'), it's better to hold onto the whole intersection
                          //          than to report that 'D' is not assignable to 'A' or 'B'.
                          //
                          //    - For a primitive type or type parameter (such as 'number = A & B') there is no point in
                          //          breaking the intersection apart.
                          if (result = someTypeRelatedToType(source, target, /*reportErrors*/ false)) {
                              return result;
                          }
                      }
                      if (target.flags & 524288 /* Union */) {
                          if (result = typeRelatedToSomeType(source, target, reportErrors && !(source.flags & 8190 /* Primitive */) && !(target.flags & 8190 /* Primitive */))) {
                              return result;
                          }
                      }
                  }
                  if (source.flags & 16384 /* TypeParameter */) {
                      var constraint = getConstraintOfTypeParameter(source);
                      if (!constraint || constraint.flags & 1 /* Any */) {
                          constraint = emptyObjectType;
                      }
                      // The constraint may need to be further instantiated with its 'this' type.
                      constraint = getTypeWithThisArgument(constraint, source);
                      // Report constraint errors only if the constraint is not the empty object type
                      var reportConstraintErrors = reportErrors && constraint !== emptyObjectType;
                      if (result = isRelatedTo(constraint, target, reportConstraintErrors)) {
                          errorInfo = saveErrorInfo;
                          return result;
                      }
                  }
                  else {
                      if (source.flags & 131072 /* Reference */ && target.flags & 131072 /* Reference */ && source.target === target.target) {
                          // We have type references to same target type, see if relationship holds for all type arguments
                          if (result = typeArgumentsRelatedTo(source, target, reportErrors)) {
                              return result;
                          }
                      }
                      // Even if relationship doesn't hold for unions, intersections, or generic type references,
                      // it may hold in a structural comparison.
                      var apparentSource = getApparentType(source);
                      // In a check of the form X = A & B, we will have previously checked if A relates to X or B relates
                      // to X. Failing both of those we want to check if the aggregation of A and B's members structurally
                      // relates to X. Thus, we include intersection types on the source side here.
                      if (apparentSource.flags & (2588672 /* ObjectType */ | 1048576 /* Intersection */) && target.flags & 2588672 /* ObjectType */) {
                          // Report structural errors only if we haven't reported any errors yet
                          var reportStructuralErrors = reportErrors && errorInfo === saveErrorInfo && !(source.flags & 8190 /* Primitive */);
                          if (result = objectTypeRelatedTo(apparentSource, source, target, reportStructuralErrors)) {
                              errorInfo = saveErrorInfo;
                              return result;
                          }
                      }
                  }
                  if (reportErrors) {
                      if (source.flags & 2588672 /* ObjectType */ && target.flags & 8190 /* Primitive */) {
                          tryElaborateErrorsForPrimitivesAndObjects(source, target);
                      }
                      reportRelationError(headMessage, source, target);
                  }
                  return 0 /* False */;
              }
              function isIdenticalTo(source, target) {
                  var result;
                  if (source.flags & 2588672 /* ObjectType */ && target.flags & 2588672 /* ObjectType */) {
                      if (source.flags & 131072 /* Reference */ && target.flags & 131072 /* Reference */ && source.target === target.target) {
                          // We have type references to same target type, see if all type arguments are identical
                          if (result = typeArgumentsRelatedTo(source, target, /*reportErrors*/ false)) {
                              return result;
                          }
                      }
                      return objectTypeRelatedTo(source, source, target, /*reportErrors*/ false);
                  }
                  if (source.flags & 524288 /* Union */ && target.flags & 524288 /* Union */ ||
                      source.flags & 1048576 /* Intersection */ && target.flags & 1048576 /* Intersection */) {
                      if (result = eachTypeRelatedToSomeType(source, target, /*reportErrors*/ false)) {
                          if (result &= eachTypeRelatedToSomeType(target, source, /*reportErrors*/ false)) {
                              return result;
                          }
                      }
                  }
                  return 0 /* False */;
              }
              // Check if a property with the given name is known anywhere in the given type. In an object type, a property
              // is considered known if the object type is empty and the check is for assignability, if the object type has
              // index signatures, or if the property is actually declared in the object type. In a union or intersection
              // type, a property is considered known if it is known in any constituent type.
              function isKnownProperty(type, name) {
                  if (type.flags & 2588672 /* ObjectType */) {
                      var resolved = resolveStructuredTypeMembers(type);
                      if ((relation === assignableRelation || relation === comparableRelation) && (type === globalObjectType || isEmptyObjectType(resolved)) ||
                          resolved.stringIndexInfo ||
                          (resolved.numberIndexInfo && isNumericLiteralName(name)) ||
                          getPropertyOfType(type, name)) {
                          return true;
                      }
                  }
                  else if (type.flags & 1572864 /* UnionOrIntersection */) {
                      for (var _i = 0, _a = type.types; _i < _a.length; _i++) {
                          var t = _a[_i];
                          if (isKnownProperty(t, name)) {
                              return true;
                          }
                      }
                  }
                  return false;
              }
              function isEmptyObjectType(t) {
                  return t.properties.length === 0 &&
                      t.callSignatures.length === 0 &&
                      t.constructSignatures.length === 0 &&
                      !t.stringIndexInfo &&
                      !t.numberIndexInfo;
              }
              function hasExcessProperties(source, target, reportErrors) {
                  if (!(target.flags & 536870912 /* ObjectLiteralPatternWithComputedProperties */) && maybeTypeOfKind(target, 2588672 /* ObjectType */)) {
                      for (var _i = 0, _a = getPropertiesOfObjectType(source); _i < _a.length; _i++) {
                          var prop = _a[_i];
                          if (!isKnownProperty(target, prop.name)) {
                              if (reportErrors) {
                                  // We know *exactly* where things went wrong when comparing the types.
                                  // Use this property as the error node as this will be more helpful in
                                  // reasoning about what went wrong.
                                  ts.Debug.assert(!!errorNode);
                                  errorNode = prop.valueDeclaration;
                                  reportError(ts.Diagnostics.Object_literal_may_only_specify_known_properties_and_0_does_not_exist_in_type_1, symbolToString(prop), typeToString(target));
                              }
                              return true;
                          }
                      }
                  }
                  return false;
              }
              function eachTypeRelatedToSomeType(source, target, reportErrors) {
                  var result = -1 /* True */;
                  var sourceTypes = source.types;
                  for (var _i = 0, sourceTypes_1 = sourceTypes; _i < sourceTypes_1.length; _i++) {
                      var sourceType = sourceTypes_1[_i];
                      var related = typeRelatedToSomeType(sourceType, target, /*reportErrors*/ false);
                      if (!related) {
                          return 0 /* False */;
                      }
                      result &= related;
                  }
                  return result;
              }
              function typeRelatedToSomeType(source, target, reportErrors) {
                  var targetTypes = target.types;
                  if (target.flags & 524288 /* Union */ && containsType(targetTypes, source)) {
                      return -1 /* True */;
                  }
                  var len = targetTypes.length;
                  for (var i = 0; i < len; i++) {
                      var related = isRelatedTo(source, targetTypes[i], reportErrors && i === len - 1);
                      if (related) {
                          return related;
                      }
                  }
                  return 0 /* False */;
              }
              function typeRelatedToEachType(source, target, reportErrors) {
                  var result = -1 /* True */;
                  var targetTypes = target.types;
                  for (var _i = 0, targetTypes_1 = targetTypes; _i < targetTypes_1.length; _i++) {
                      var targetType = targetTypes_1[_i];
                      var related = isRelatedTo(source, targetType, reportErrors);
                      if (!related) {
                          return 0 /* False */;
                      }
                      result &= related;
                  }
                  return result;
              }
              function someTypeRelatedToType(source, target, reportErrors) {
                  var sourceTypes = source.types;
                  if (source.flags & 524288 /* Union */ && containsType(sourceTypes, target)) {
                      return -1 /* True */;
                  }
                  var len = sourceTypes.length;
                  for (var i = 0; i < len; i++) {
                      var related = isRelatedTo(sourceTypes[i], target, reportErrors && i === len - 1);
                      if (related) {
                          return related;
                      }
                  }
                  return 0 /* False */;
              }
              function eachTypeRelatedToType(source, target, reportErrors) {
                  var result = -1 /* True */;
                  var sourceTypes = source.types;
                  for (var _i = 0, sourceTypes_2 = sourceTypes; _i < sourceTypes_2.length; _i++) {
                      var sourceType = sourceTypes_2[_i];
                      var related = isRelatedTo(sourceType, target, reportErrors);
                      if (!related) {
                          return 0 /* False */;
                      }
                      result &= related;
                  }
                  return result;
              }
              function typeArgumentsRelatedTo(source, target, reportErrors) {
                  var sources = source.typeArguments || emptyArray;
                  var targets = target.typeArguments || emptyArray;
                  if (sources.length !== targets.length && relation === identityRelation) {
                      return 0 /* False */;
                  }
                  var length = sources.length <= targets.length ? sources.length : targets.length;
                  var result = -1 /* True */;
                  for (var i = 0; i < length; i++) {
                      var related = isRelatedTo(sources[i], targets[i], reportErrors);
                      if (!related) {
                          return 0 /* False */;
                      }
                      result &= related;
                  }
                  return result;
              }
              // Determine if two object types are related by structure. First, check if the result is already available in the global cache.
              // Second, check if we have already started a comparison of the given two types in which case we assume the result to be true.
              // Third, check if both types are part of deeply nested chains of generic type instantiations and if so assume the types are
              // equal and infinitely expanding. Fourth, if we have reached a depth of 100 nested comparisons, assume we have runaway recursion
              // and issue an error. Otherwise, actually compare the structure of the two types.
              function objectTypeRelatedTo(source, originalSource, target, reportErrors) {
                  if (overflow) {
                      return 0 /* False */;
                  }
                  var id = relation !== identityRelation || source.id < target.id ? source.id + "," + target.id : target.id + "," + source.id;
                  var related = relation[id];
                  if (related !== undefined) {
                      if (reportErrors && related === 2 /* Failed */) {
                          // We are elaborating errors and the cached result is an unreported failure. Record the result as a reported
                          // failure and continue computing the relation such that errors get reported.
                          relation[id] = 3 /* FailedAndReported */;
                      }
                      else {
                          return related === 1 /* Succeeded */ ? -1 /* True */ : 0 /* False */;
                      }
                  }
                  if (depth > 0) {
                      for (var i = 0; i < depth; i++) {
                          // If source and target are already being compared, consider them related with assumptions
                          if (maybeStack[i][id]) {
                              return 1 /* Maybe */;
                          }
                      }
                      if (depth === 100) {
                          overflow = true;
                          return 0 /* False */;
                      }
                  }
                  else {
                      sourceStack = [];
                      targetStack = [];
                      maybeStack = [];
                      expandingFlags = 0;
                  }
                  sourceStack[depth] = source;
                  targetStack[depth] = target;
                  maybeStack[depth] = ts.createMap();
                  maybeStack[depth][id] = 1 /* Succeeded */;
                  depth++;
                  var saveExpandingFlags = expandingFlags;
                  if (!(expandingFlags & 1) && isDeeplyNestedGeneric(source, sourceStack, depth))
                      expandingFlags |= 1;
                  if (!(expandingFlags & 2) && isDeeplyNestedGeneric(target, targetStack, depth))
                      expandingFlags |= 2;
                  var result;
                  if (expandingFlags === 3) {
                      result = 1 /* Maybe */;
                  }
                  else {
                      result = propertiesRelatedTo(source, target, reportErrors);
                      if (result) {
                          result &= signaturesRelatedTo(source, target, 0 /* Call */, reportErrors);
                          if (result) {
                              result &= signaturesRelatedTo(source, target, 1 /* Construct */, reportErrors);
                              if (result) {
                                  result &= indexTypesRelatedTo(source, originalSource, target, 0 /* String */, reportErrors);
                                  if (result) {
                                      result &= indexTypesRelatedTo(source, originalSource, target, 1 /* Number */, reportErrors);
                                  }
                              }
                          }
                      }
                  }
                  expandingFlags = saveExpandingFlags;
                  depth--;
                  if (result) {
                      var maybeCache = maybeStack[depth];
                      // If result is definitely true, copy assumptions to global cache, else copy to next level up
                      var destinationCache = (result === -1 /* True */ || depth === 0) ? relation : maybeStack[depth - 1];
                      ts.copyProperties(maybeCache, destinationCache);
                  }
                  else {
                      // A false result goes straight into global cache (when something is false under assumptions it
                      // will also be false without assumptions)
                      relation[id] = reportErrors ? 3 /* FailedAndReported */ : 2 /* Failed */;
                  }
                  return result;
              }
              function propertiesRelatedTo(source, target, reportErrors) {
                  if (relation === identityRelation) {
                      return propertiesIdenticalTo(source, target);
                  }
                  var result = -1 /* True */;
                  var properties = getPropertiesOfObjectType(target);
                  var requireOptionalProperties = relation === subtypeRelation && !(source.flags & 8388608 /* ObjectLiteral */);
                  for (var _i = 0, properties_1 = properties; _i < properties_1.length; _i++) {
                      var targetProp = properties_1[_i];
                      var sourceProp = getPropertyOfType(source, targetProp.name);
                      if (sourceProp !== targetProp) {
                          if (!sourceProp) {
                              if (!(targetProp.flags & 536870912 /* Optional */) || requireOptionalProperties) {
                                  if (reportErrors) {
                                      reportError(ts.Diagnostics.Property_0_is_missing_in_type_1, symbolToString(targetProp), typeToString(source));
                                  }
                                  return 0 /* False */;
                              }
                          }
                          else if (!(targetProp.flags & 134217728 /* Prototype */)) {
                              var sourcePropFlags = getDeclarationFlagsFromSymbol(sourceProp);
                              var targetPropFlags = getDeclarationFlagsFromSymbol(targetProp);
                              if (sourcePropFlags & 8 /* Private */ || targetPropFlags & 8 /* Private */) {
                                  if (sourceProp.valueDeclaration !== targetProp.valueDeclaration) {
                                      if (reportErrors) {
                                          if (sourcePropFlags & 8 /* Private */ && targetPropFlags & 8 /* Private */) {
                                              reportError(ts.Diagnostics.Types_have_separate_declarations_of_a_private_property_0, symbolToString(targetProp));
                                          }
                                          else {
                                              reportError(ts.Diagnostics.Property_0_is_private_in_type_1_but_not_in_type_2, symbolToString(targetProp), typeToString(sourcePropFlags & 8 /* Private */ ? source : target), typeToString(sourcePropFlags & 8 /* Private */ ? target : source));
                                          }
                                      }
                                      return 0 /* False */;
                                  }
                              }
                              else if (targetPropFlags & 16 /* Protected */) {
                                  var sourceDeclaredInClass = sourceProp.parent && sourceProp.parent.flags & 32 /* Class */;
                                  var sourceClass = sourceDeclaredInClass ? getDeclaredTypeOfSymbol(getParentOfSymbol(sourceProp)) : undefined;
                                  var targetClass = getDeclaredTypeOfSymbol(getParentOfSymbol(targetProp));
                                  if (!sourceClass || !hasBaseType(sourceClass, targetClass)) {
                                      if (reportErrors) {
                                          reportError(ts.Diagnostics.Property_0_is_protected_but_type_1_is_not_a_class_derived_from_2, symbolToString(targetProp), typeToString(sourceClass || source), typeToString(targetClass));
                                      }
                                      return 0 /* False */;
                                  }
                              }
                              else if (sourcePropFlags & 16 /* Protected */) {
                                  if (reportErrors) {
                                      reportError(ts.Diagnostics.Property_0_is_protected_in_type_1_but_public_in_type_2, symbolToString(targetProp), typeToString(source), typeToString(target));
                                  }
                                  return 0 /* False */;
                              }
                              var related = isRelatedTo(getTypeOfSymbol(sourceProp), getTypeOfSymbol(targetProp), reportErrors);
                              if (!related) {
                                  if (reportErrors) {
                                      reportError(ts.Diagnostics.Types_of_property_0_are_incompatible, symbolToString(targetProp));
                                  }
                                  return 0 /* False */;
                              }
                              result &= related;
                              if (sourceProp.flags & 536870912 /* Optional */ && !(targetProp.flags & 536870912 /* Optional */)) {
                                  // TypeScript 1.0 spec (April 2014): 3.8.3
                                  // S is a subtype of a type T, and T is a supertype of S if ...
                                  // S' and T are object types and, for each member M in T..
                                  // M is a property and S' contains a property N where
                                  // if M is a required property, N is also a required property
                                  // (M - property in T)
                                  // (N - property in S)
                                  if (reportErrors) {
                                      reportError(ts.Diagnostics.Property_0_is_optional_in_type_1_but_required_in_type_2, symbolToString(targetProp), typeToString(source), typeToString(target));
                                  }
                                  return 0 /* False */;
                              }
                          }
                      }
                  }
                  return result;
              }
              function propertiesIdenticalTo(source, target) {
                  if (!(source.flags & 2588672 /* ObjectType */ && target.flags & 2588672 /* ObjectType */)) {
                      return 0 /* False */;
                  }
                  var sourceProperties = getPropertiesOfObjectType(source);
                  var targetProperties = getPropertiesOfObjectType(target);
                  if (sourceProperties.length !== targetProperties.length) {
                      return 0 /* False */;
                  }
                  var result = -1 /* True */;
                  for (var _i = 0, sourceProperties_1 = sourceProperties; _i < sourceProperties_1.length; _i++) {
                      var sourceProp = sourceProperties_1[_i];
                      var targetProp = getPropertyOfObjectType(target, sourceProp.name);
                      if (!targetProp) {
                          return 0 /* False */;
                      }
                      var related = compareProperties(sourceProp, targetProp, isRelatedTo);
                      if (!related) {
                          return 0 /* False */;
                      }
                      result &= related;
                  }
                  return result;
              }
              function signaturesRelatedTo(source, target, kind, reportErrors) {
                  if (relation === identityRelation) {
                      return signaturesIdenticalTo(source, target, kind);
                  }
                  if (target === anyFunctionType || source === anyFunctionType) {
                      return -1 /* True */;
                  }
                  var sourceSignatures = getSignaturesOfType(source, kind);
                  var targetSignatures = getSignaturesOfType(target, kind);
                  if (kind === 1 /* Construct */ && sourceSignatures.length && targetSignatures.length) {
                      if (isAbstractConstructorType(source) && !isAbstractConstructorType(target)) {
                          // An abstract constructor type is not assignable to a non-abstract constructor type
                          // as it would otherwise be possible to new an abstract class. Note that the assignability
                          // check we perform for an extends clause excludes construct signatures from the target,
                          // so this check never proceeds.
                          if (reportErrors) {
                              reportError(ts.Diagnostics.Cannot_assign_an_abstract_constructor_type_to_a_non_abstract_constructor_type);
                          }
                          return 0 /* False */;
                      }
                      if (!constructorVisibilitiesAreCompatible(sourceSignatures[0], targetSignatures[0], reportErrors)) {
                          return 0 /* False */;
                      }
                  }
                  var result = -1 /* True */;
                  var saveErrorInfo = errorInfo;
                  outer: for (var _i = 0, targetSignatures_1 = targetSignatures; _i < targetSignatures_1.length; _i++) {
                      var t = targetSignatures_1[_i];
                      // Only elaborate errors from the first failure
                      var shouldElaborateErrors = reportErrors;
                      for (var _a = 0, sourceSignatures_1 = sourceSignatures; _a < sourceSignatures_1.length; _a++) {
                          var s = sourceSignatures_1[_a];
                          var related = signatureRelatedTo(s, t, shouldElaborateErrors);
                          if (related) {
                              result &= related;
                              errorInfo = saveErrorInfo;
                              continue outer;
                          }
                          shouldElaborateErrors = false;
                      }
                      if (shouldElaborateErrors) {
                          reportError(ts.Diagnostics.Type_0_provides_no_match_for_the_signature_1, typeToString(source), signatureToString(t, /*enclosingDeclaration*/ undefined, /*flags*/ undefined, kind));
                      }
                      return 0 /* False */;
                  }
                  return result;
              }
              /**
               * See signatureAssignableTo, compareSignaturesIdentical
               */
              function signatureRelatedTo(source, target, reportErrors) {
                  return compareSignaturesRelated(source, target, /*ignoreReturnTypes*/ false, reportErrors, reportError, isRelatedTo);
              }
              function signaturesIdenticalTo(source, target, kind) {
                  var sourceSignatures = getSignaturesOfType(source, kind);
                  var targetSignatures = getSignaturesOfType(target, kind);
                  if (sourceSignatures.length !== targetSignatures.length) {
                      return 0 /* False */;
                  }
                  var result = -1 /* True */;
                  for (var i = 0, len = sourceSignatures.length; i < len; i++) {
                      var related = compareSignaturesIdentical(sourceSignatures[i], targetSignatures[i], /*partialMatch*/ false, /*ignoreThisTypes*/ false, /*ignoreReturnTypes*/ false, isRelatedTo);
                      if (!related) {
                          return 0 /* False */;
                      }
                      result &= related;
                  }
                  return result;
              }
              function eachPropertyRelatedTo(source, target, kind, reportErrors) {
                  var result = -1 /* True */;
                  for (var _i = 0, _a = getPropertiesOfObjectType(source); _i < _a.length; _i++) {
                      var prop = _a[_i];
                      if (kind === 0 /* String */ || isNumericLiteralName(prop.name)) {
                          var related = isRelatedTo(getTypeOfSymbol(prop), target, reportErrors);
                          if (!related) {
                              if (reportErrors) {
                                  reportError(ts.Diagnostics.Property_0_is_incompatible_with_index_signature, symbolToString(prop));
                              }
                              return 0 /* False */;
                          }
                          result &= related;
                      }
                  }
                  return result;
              }
              function indexInfoRelatedTo(sourceInfo, targetInfo, reportErrors) {
                  var related = isRelatedTo(sourceInfo.type, targetInfo.type, reportErrors);
                  if (!related && reportErrors) {
                      reportError(ts.Diagnostics.Index_signatures_are_incompatible);
                  }
                  return related;
              }
              function indexTypesRelatedTo(source, originalSource, target, kind, reportErrors) {
                  if (relation === identityRelation) {
                      return indexTypesIdenticalTo(source, target, kind);
                  }
                  var targetInfo = getIndexInfoOfType(target, kind);
                  if (!targetInfo || ((targetInfo.type.flags & 1 /* Any */) && !(originalSource.flags & 8190 /* Primitive */))) {
                      // Index signature of type any permits assignment from everything but primitives
                      return -1 /* True */;
                  }
                  var sourceInfo = getIndexInfoOfType(source, kind) ||
                      kind === 1 /* Number */ && getIndexInfoOfType(source, 0 /* String */);
                  if (sourceInfo) {
                      return indexInfoRelatedTo(sourceInfo, targetInfo, reportErrors);
                  }
                  if (isObjectLiteralType(source)) {
                      var related = -1 /* True */;
                      if (kind === 0 /* String */) {
                          var sourceNumberInfo = getIndexInfoOfType(source, 1 /* Number */);
                          if (sourceNumberInfo) {
                              related = indexInfoRelatedTo(sourceNumberInfo, targetInfo, reportErrors);
                          }
                      }
                      if (related) {
                          related &= eachPropertyRelatedTo(source, targetInfo.type, kind, reportErrors);
                      }
                      return related;
                  }
                  if (reportErrors) {
                      reportError(ts.Diagnostics.Index_signature_is_missing_in_type_0, typeToString(source));
                  }
                  return 0 /* False */;
              }
              function indexTypesIdenticalTo(source, target, indexKind) {
                  var targetInfo = getIndexInfoOfType(target, indexKind);
                  var sourceInfo = getIndexInfoOfType(source, indexKind);
                  if (!sourceInfo && !targetInfo) {
                      return -1 /* True */;
                  }
                  if (sourceInfo && targetInfo && sourceInfo.isReadonly === targetInfo.isReadonly) {
                      return isRelatedTo(sourceInfo.type, targetInfo.type);
                  }
                  return 0 /* False */;
              }
              function constructorVisibilitiesAreCompatible(sourceSignature, targetSignature, reportErrors) {
                  if (!sourceSignature.declaration || !targetSignature.declaration) {
                      return true;
                  }
                  var sourceAccessibility = sourceSignature.declaration.flags & (8 /* Private */ | 16 /* Protected */);
                  var targetAccessibility = targetSignature.declaration.flags & (8 /* Private */ | 16 /* Protected */);
                  // A public, protected and private signature is assignable to a private signature.
                  if (targetAccessibility === 8 /* Private */) {
                      return true;
                  }
                  // A public and protected signature is assignable to a protected signature.
                  if (targetAccessibility === 16 /* Protected */ && sourceAccessibility !== 8 /* Private */) {
                      return true;
                  }
                  // Only a public signature is assignable to public signature.
                  if (targetAccessibility !== 16 /* Protected */ && !sourceAccessibility) {
                      return true;
                  }
                  if (reportErrors) {
                      reportError(ts.Diagnostics.Cannot_assign_a_0_constructor_type_to_a_1_constructor_type, visibilityToString(sourceAccessibility), visibilityToString(targetAccessibility));
                  }
                  return false;
              }
          }