ts-json-schema-generator
Version:
Generate JSON schema from your Typescript sources
210 lines • 9.67 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.isAssignableTo = void 0;
const AnyType_js_1 = require("../Type/AnyType.js");
const ArrayType_js_1 = require("../Type/ArrayType.js");
const EnumType_js_1 = require("../Type/EnumType.js");
const IntersectionType_js_1 = require("../Type/IntersectionType.js");
const NullType_js_1 = require("../Type/NullType.js");
const ObjectType_js_1 = require("../Type/ObjectType.js");
const OptionalType_js_1 = require("../Type/OptionalType.js");
const TupleType_js_1 = require("../Type/TupleType.js");
const UndefinedType_js_1 = require("../Type/UndefinedType.js");
const UnionType_js_1 = require("../Type/UnionType.js");
const UnknownType_js_1 = require("../Type/UnknownType.js");
const VoidType_js_1 = require("../Type/VoidType.js");
const derefType_js_1 = require("./derefType.js");
const LiteralType_js_1 = require("../Type/LiteralType.js");
const StringType_js_1 = require("../Type/StringType.js");
const NumberType_js_1 = require("../Type/NumberType.js");
const BooleanType_js_1 = require("../Type/BooleanType.js");
const InferType_js_1 = require("../Type/InferType.js");
const RestType_js_1 = require("../Type/RestType.js");
const NeverType_js_1 = require("../Type/NeverType.js");
function combineIntersectingTypes(intersection) {
const objectTypes = [];
const combined = intersection.getTypes().filter((type) => {
if (type instanceof ObjectType_js_1.ObjectType) {
objectTypes.push(type);
}
else {
return true;
}
return false;
});
if (objectTypes.length === 1) {
combined.push(objectTypes[0]);
}
else if (objectTypes.length > 1) {
combined.push(new ObjectType_js_1.ObjectType(`combined-objects-${intersection.getId()}`, objectTypes, [], false));
}
return combined;
}
function getObjectProperties(type) {
type = (0, derefType_js_1.derefType)(type);
const properties = [];
if (type instanceof ObjectType_js_1.ObjectType) {
properties.push(...type.getProperties());
for (const baseType of type.getBaseTypes()) {
properties.push(...getObjectProperties(baseType));
}
}
return properties;
}
function getPrimitiveType(value) {
switch (typeof value) {
case "string":
return new StringType_js_1.StringType();
case "number":
return new NumberType_js_1.NumberType();
case "boolean":
return new BooleanType_js_1.BooleanType();
}
}
function isAssignableTo(target, source, inferMap = new Map(), insideTypes = new Set()) {
source = (0, derefType_js_1.derefType)(source);
target = (0, derefType_js_1.derefType)(target);
if (source instanceof NeverType_js_1.NeverType) {
return true;
}
if (target instanceof NeverType_js_1.NeverType) {
return false;
}
if (target instanceof InferType_js_1.InferType) {
const key = target.getName();
const infer = inferMap.get(key);
if (infer === undefined) {
inferMap.set(key, source);
}
else {
inferMap.set(key, new UnionType_js_1.UnionType([infer, source]));
}
return true;
}
if (source.getId() === target.getId()) {
return true;
}
if (insideTypes.has(source) || insideTypes.has(target)) {
return true;
}
if (source instanceof AnyType_js_1.AnyType || target instanceof AnyType_js_1.AnyType) {
return true;
}
if (target instanceof UnknownType_js_1.UnknownType) {
return true;
}
if (target instanceof VoidType_js_1.VoidType) {
return source instanceof NullType_js_1.NullType || source instanceof UndefinedType_js_1.UndefinedType;
}
if (source instanceof UnionType_js_1.UnionType || source instanceof EnumType_js_1.EnumType) {
return source.getTypes().every((type) => isAssignableTo(target, type, inferMap, insideTypes));
}
if (source instanceof IntersectionType_js_1.IntersectionType) {
return combineIntersectingTypes(source).some((type) => isAssignableTo(target, type, inferMap, insideTypes));
}
if (target instanceof ArrayType_js_1.ArrayType) {
const targetItemType = target.getItem();
if (source instanceof ArrayType_js_1.ArrayType) {
return isAssignableTo(targetItemType, source.getItem(), inferMap, insideTypes);
}
else if (source instanceof TupleType_js_1.TupleType) {
return isAssignableTo(targetItemType, new UnionType_js_1.UnionType(source.getTypes()), inferMap, insideTypes);
}
else {
return false;
}
}
if (target instanceof UnionType_js_1.UnionType || target instanceof EnumType_js_1.EnumType) {
return target.getTypes().some((type) => isAssignableTo(type, source, inferMap, insideTypes));
}
if (target instanceof IntersectionType_js_1.IntersectionType) {
return combineIntersectingTypes(target).every((type) => isAssignableTo(type, source, inferMap, insideTypes));
}
if (source instanceof LiteralType_js_1.LiteralType) {
return isAssignableTo(target, getPrimitiveType(source.getValue()), inferMap);
}
if (target instanceof ObjectType_js_1.ObjectType) {
if (target.getNonPrimitive() &&
(source instanceof NumberType_js_1.NumberType || source instanceof StringType_js_1.StringType || source instanceof BooleanType_js_1.BooleanType)) {
return false;
}
const targetMembers = getObjectProperties(target);
if (targetMembers.length === 0) {
return !isAssignableTo(new UnionType_js_1.UnionType([new UndefinedType_js_1.UndefinedType(), new NullType_js_1.NullType()]), source, inferMap, insideTypes);
}
else if (source instanceof ObjectType_js_1.ObjectType) {
const sourceMembers = getObjectProperties(source);
const inCommon = targetMembers.some((targetMember) => sourceMembers.some((sourceMember) => targetMember.getName() === sourceMember.getName()));
return (targetMembers.every((targetMember) => {
const sourceMember = sourceMembers.find((member) => targetMember.getName() === member.getName());
return sourceMember == null ? inCommon && !targetMember.isRequired() : true;
}) &&
sourceMembers.every((sourceMember) => {
const targetMember = targetMembers.find((member) => member.getName() === sourceMember.getName());
if (targetMember == null) {
return true;
}
return isAssignableTo(targetMember.getType(), sourceMember.getType(), inferMap, new Set(insideTypes).add(source).add(target));
}));
}
const isArrayLikeType = source instanceof ArrayType_js_1.ArrayType || source instanceof TupleType_js_1.TupleType;
if (isArrayLikeType) {
const lengthPropType = targetMembers
.find((prop) => prop.getName() === "length" && prop.isRequired())
?.getType();
if (source instanceof ArrayType_js_1.ArrayType) {
return lengthPropType instanceof NumberType_js_1.NumberType;
}
if (source instanceof TupleType_js_1.TupleType) {
if (lengthPropType instanceof LiteralType_js_1.LiteralType) {
const types = source.getTypes();
const lengthPropValue = lengthPropType.getValue();
return types.length === lengthPropValue;
}
}
}
}
if (target instanceof TupleType_js_1.TupleType) {
if (source instanceof TupleType_js_1.TupleType) {
const sourceMembers = source.getTypes();
const targetMembers = target.getTypes();
return targetMembers.every((targetMember, i) => {
const numTarget = targetMembers.length;
const numSource = sourceMembers.length;
if (i == numTarget - 1) {
if (numTarget <= numSource + 1) {
if (targetMember instanceof RestType_js_1.RestType) {
const remaining = [];
for (let j = i; j < numSource; j++) {
remaining.push(sourceMembers[j]);
}
return isAssignableTo(targetMember.getType(), new TupleType_js_1.TupleType(remaining), inferMap, insideTypes);
}
else if (numTarget < numSource) {
return false;
}
}
}
const sourceMember = sourceMembers[i];
if (targetMember instanceof OptionalType_js_1.OptionalType) {
if (sourceMember) {
return (isAssignableTo(targetMember, sourceMember, inferMap, insideTypes) ||
isAssignableTo(targetMember.getType(), sourceMember, inferMap, insideTypes));
}
else {
return true;
}
}
else {
if (sourceMember === undefined) {
return false;
}
return isAssignableTo(targetMember, sourceMember, inferMap, insideTypes);
}
});
}
}
return false;
}
exports.isAssignableTo = isAssignableTo;
//# sourceMappingURL=isAssignableTo.js.map
;