export function isSuperType()

in packages/jsii-diff/lib/type-analysis.ts [14:100]


export function isSuperType(
  a: reflect.TypeReference,
  b: reflect.TypeReference,
  updatedSystem: reflect.TypeSystem,
): Analysis {
  if (a.void || b.void) {
    throw new Error('isSuperType() does not handle voids');
  }
  if (a.isAny) {
    return { success: true };
  }

  if (a.primitive !== undefined) {
    if (a.primitive === b.primitive) {
      return { success: true };
    }
    return failure(`${b.toString()} is not assignable to ${a.toString()}`);
  }

  if (a.arrayOfType !== undefined) {
    // Arrays are covariant
    if (b.arrayOfType === undefined) {
      return failure(`${b.toString()} is not an array type`);
    }
    return prependReason(
      isSuperType(a.arrayOfType, b.arrayOfType, updatedSystem),
      `${b.toString()} is not assignable to ${a.toString()}`,
    );
  }

  if (a.mapOfType !== undefined) {
    // Maps are covariant (are they?)
    if (b.mapOfType === undefined) {
      return failure(`${b.toString()} is not a map type`);
    }
    return prependReason(
      isSuperType(a.mapOfType, b.mapOfType, updatedSystem),
      `${b.toString()} is not assignable to ${a.toString()}`,
    );
  }

  // Every element of B can be assigned to A
  if (b.unionOfTypes !== undefined) {
    const analyses = b.unionOfTypes.map((bbb) =>
      isSuperType(a, bbb, updatedSystem),
    );
    if (analyses.every((x) => x.success)) {
      return { success: true };
    }
    return failure(
      `some of ${b.toString()} are not assignable to ${a.toString()}`,
      ...flatMap(analyses, (x) => (x.success ? [] : x.reasons)),
    );
  }
  // There should be an element of A which can accept all of B
  if (a.unionOfTypes !== undefined) {
    const analyses = a.unionOfTypes.map((aaa) =>
      isSuperType(aaa, b, updatedSystem),
    );
    if (analyses.some((x) => x.success)) {
      return { success: true };
    }
    return failure(
      `none of ${b.toString()} are assignable to ${a.toString()}`,
      ...flatMap(analyses, (x) => (x.success ? [] : x.reasons)),
    );
  }

  // We have two named types, recursion might happen so protect against it.
  try {
    // For named types, we'll always do a nominal typing relationship.
    // That is, if in the updated typesystem someone were to use the type name
    // from the old assembly, do they have a typing relationship that's accepted
    // by a nominal type system. (That check also rules out enums)
    const nominalCheck = isNominalSuperType(a, b, updatedSystem);
    if (nominalCheck.success === false) {
      return nominalCheck;
    }

    // At this point, the only thing left to do is recurse into the structs.
    // We used to do that here, but we don't anymore; structs check themselves
    // for structural weakening/strengthening.
    return { success: true };
  } catch (e: any) {
    return failure(e.message);
  }
}