joi-to-typescript
Version:
Convert Joi Schemas to TypeScript interfaces
667 lines (666 loc) • 31.5 kB
JavaScript
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.supportedJoiTypes = void 0;
exports.getAllCustomTypes = getAllCustomTypes;
exports.typeContentToTs = typeContentToTs;
exports.parseSchema = parseSchema;
const utils_1 = require("./utils");
const types_1 = require("./types");
const joiUtils_1 = require("./joiUtils");
const write_1 = require("./write");
const node_util_1 = __importDefault(require("node:util")); // see __tests__/joiTypes.ts for more information
// see __tests__/joiTypes.ts for more information
exports.supportedJoiTypes = ['array', 'object', 'alternatives', 'any', 'boolean', 'date', 'number', 'string'];
// @TODO - Temporarily used prevent 'map' and 'set' from being used by cast
// Remove once support for 'map' and 'set' is added
const validCastTo = ['string', 'number'];
function getCommonDetails(details, settings) {
var _a, _b, _c;
const interfaceOrTypeName = (0, joiUtils_1.getInterfaceOrTypeName)(settings, details);
const description = (_a = details.flags) === null || _a === void 0 ? void 0 : _a.description;
const presence = (_b = details.flags) === null || _b === void 0 ? void 0 : _b.presence;
let value = (_c = details.flags) === null || _c === void 0 ? void 0 : _c.default;
if (value &&
typeof value === 'object' &&
'special' in value &&
value.special === 'deep' &&
Object.keys(value).length === 1) {
// Special case. When using the empty `default()` function on
// a schema entry, Joi adds a special symbol to the entry, which
// is converted to {"special": "deep"} via describe.
// When this case comes up, we can ignore it.
// Ref: https://github.com/hapijs/joi/blob/e7e9c5d18dafaa510a7ece02c225653db5fc998f/lib/manifest.js#L179
value = undefined;
}
const defaultJsDoc = settings.supplyDefaultsInJsDoc && value !== undefined ? node_util_1.default.inspect(value, { depth: null }) : undefined;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const examples = (details.examples || [])
.filter(e => e !== undefined)
.map(example => {
return typeof example === 'object'
? // Joi accepts `any` as type for an example
JSON.stringify(example, null, 2)
: example.toString();
});
const isReadonly = (0, joiUtils_1.getIsReadonly)(details);
const disableJsDoc = (0, joiUtils_1.getDisableDescription)(details);
let required;
if (presence === 'required' ||
(settings.treatDefaultedOptionalAsRequired && presence !== 'optional' && value !== undefined)) {
required = true;
}
else if (presence === 'optional') {
required = false;
}
else {
required = settings.defaultToRequired;
}
return {
interfaceOrTypeName,
jsDoc: { description, examples, default: defaultJsDoc, disable: disableJsDoc },
required,
value,
isReadonly
};
}
function getAllCustomTypes(parsedSchema) {
var _a;
const customTypes = [];
if (parsedSchema.__isRoot) {
customTypes.push(...parsedSchema.children.flatMap(child => getAllCustomTypes(child)));
}
else {
customTypes.push(...((_a = parsedSchema.customTypes) !== null && _a !== void 0 ? _a : []));
}
return customTypes;
}
function getDefaultTypeTsContent(settings, indentLevel, parsedSchema, tsContent) {
if (!settings.unionNewLine) {
return `${JSON.stringify(parsedSchema.value)} | ${tsContent}`;
}
const indent = (0, write_1.getIndentStr)(settings, indentLevel);
return '\n' + indent + '| ' + JSON.stringify(parsedSchema.value) + '\n' + indent + '| ' + tsContent;
}
function typeContentToTsHelper(settings, parsedSchema, indentLevel, doExport = false) {
if (!parsedSchema.__isRoot) {
const tsContent = settings.supplyDefaultsInType
? parsedSchema.value !== undefined
? getDefaultTypeTsContent(settings, indentLevel, parsedSchema, parsedSchema.content)
: parsedSchema.content
: parsedSchema.content;
if (doExport) {
return {
tsContent: `export type ${parsedSchema.interfaceOrTypeName} = ${tsContent};`,
jsDoc: parsedSchema.jsDoc
};
}
return {
tsContent,
jsDoc: parsedSchema.jsDoc
};
}
const children = parsedSchema.children;
if (doExport && !parsedSchema.interfaceOrTypeName) {
// Cannot figured a way to make this error happen
/* istanbul ignore next */
throw new Error(`Type ${JSON.stringify(parsedSchema)} needs a name to be exported`);
}
switch (parsedSchema.joinOperation) {
case 'list': {
const childrenContent = children.map(child => typeContentToTsHelper(settings, child, indentLevel));
if (childrenContent.length > 1) {
/* istanbul ignore next */
throw new Error('Multiple array item types not supported');
}
let content = childrenContent[0].tsContent;
if (content.includes('|')) {
// TODO: might need a better way to add the parens for union
content = `(${content})`;
}
const arrayStr = settings.supplyDefaultsInType
? parsedSchema.value !== undefined
? getDefaultTypeTsContent(settings, indentLevel, parsedSchema, `${content}[]`)
: `${content}[]`
: `${content}[]`;
if (doExport) {
return {
tsContent: `export type ${parsedSchema.interfaceOrTypeName} = ${arrayStr};`,
jsDoc: parsedSchema.jsDoc
};
}
return { tsContent: arrayStr, jsDoc: parsedSchema.jsDoc };
}
case 'tuple':
case 'union': {
const isTuple = parsedSchema.joinOperation === 'tuple';
const indentString = (0, write_1.getIndentStr)(settings, indentLevel);
const itemSeparatorBeforeItem = isTuple ? '' : ' |';
const itemSeparatorAfterItem = isTuple ? ',' : '';
const itemSeparatorAfterNewline = isTuple ? '' : '|';
let hasOneDescription = false;
let finalStr;
const childrenContent = [];
let first = true;
let previousIsInline = false;
if (settings.supplyDefaultsInType && parsedSchema.value !== undefined) {
if (settings.unionNewLine) {
childrenContent.push('\n' + indentString + '| ' + JSON.stringify(parsedSchema.value));
previousIsInline = false;
}
else {
childrenContent.push(JSON.stringify(parsedSchema.value));
previousIsInline = true;
}
first = false;
}
for (let itemIdx = 0; itemIdx < children.length; itemIdx++) {
const child = children[itemIdx];
const childInfo = typeContentToTsHelper(settings, child,
// Special case for objects because their contents need to be indented once more
child.__isRoot && ['object', 'list', 'tuple'].includes(child.joinOperation) ? indentLevel + 1 : indentLevel);
const descriptionStr = (0, write_1.getJsDocString)(settings, child.interfaceOrTypeName, childInfo.jsDoc, indentLevel);
hasOneDescription || (hasOneDescription = descriptionStr !== '');
// Prevents test failures because of spaces at line endings
let childInfoTsContentPrefix = '';
if (isTuple) {
if (previousIsInline) {
childInfoTsContentPrefix = ' ';
}
}
else {
childInfoTsContentPrefix = childInfo.tsContent.startsWith('\n') ? '' : ' ';
}
/*
Compose the child code line. If there is a description, it must be above the entry.
*/
let childContent = childInfo.tsContent;
let itemPrefixWithIndent = indentString + itemSeparatorAfterNewline;
let skipNewline = false;
if (childContent.includes('|')) {
childContent = `(${childContent})`;
}
if (isTuple) {
childContent += child.required ? '' : '?';
}
else {
// Make sure we don't repeat by accident multiple | when joining unions
if (settings.unionNewLine && childContent.trimStart().startsWith('|')) {
itemPrefixWithIndent = '';
skipNewline = true;
}
}
childContent += itemIdx < children.length - 1 ? itemSeparatorAfterItem : '';
if (descriptionStr !== '' ||
(children.length > 1 && ((!isTuple && settings.unionNewLine) || (isTuple && settings.tupleNewLine)))) {
// If there is a description it means we also have a new line, which means
// we need to properly indent the following line too.
let prefix = descriptionStr;
if (prefix === '') {
if (first) {
prefix = '';
}
else {
prefix = skipNewline ? '' : '\n';
}
}
let tsContentPrefix = childInfoTsContentPrefix;
if (tsContentPrefix === '') {
// Handle the case where we are wrapping the child content, and we need
// to make some space between the union operator and the content
if (itemPrefixWithIndent.endsWith('|') && childContent.startsWith('(')) {
tsContentPrefix = ' ';
}
}
childrenContent.push((first ? (skipNewline ? '' : '\n') : '') +
`${prefix}${itemPrefixWithIndent}${tsContentPrefix}${childContent}`);
previousIsInline = false;
}
else {
// Normal inline content
childrenContent.push((first
? ''
: (previousIsInline ? itemSeparatorBeforeItem : itemPrefixWithIndent) + childInfoTsContentPrefix) +
childContent);
previousIsInline = true;
}
first = false;
}
finalStr = childrenContent.join(hasOneDescription ? '\n' : '');
if (isTuple) {
finalStr = `[${finalStr}${hasOneDescription ? '\n' + (0, write_1.getIndentStr)(settings, indentLevel - 1) : ''}${settings.tupleNewLine ? '\n' + (0, write_1.getIndentStr)(settings, indentLevel - 1) : ''}]`;
}
if (doExport) {
return {
tsContent: `export type ${parsedSchema.interfaceOrTypeName} =${
// Prevents test failures because of spaces at line endings
finalStr.startsWith('\n') ? '' : ' '}${finalStr};`,
jsDoc: parsedSchema.jsDoc
};
}
return { tsContent: finalStr, jsDoc: parsedSchema.jsDoc };
}
case 'objectWithUndefinedKeys':
case 'object': {
if (!children.length && !doExport) {
if (parsedSchema.joinOperation === 'objectWithUndefinedKeys') {
return { tsContent: 'object', jsDoc: parsedSchema.jsDoc };
}
else {
return { tsContent: 'Record<string, never>', jsDoc: parsedSchema.jsDoc };
}
}
// interface can have no properties {} if the joi object has none defined
let objectStr = '{}';
let hasDefault = false;
if (children.length !== 0) {
const childrenContent = children.map(child => {
const childInfo = typeContentToTsHelper(settings, child, indentLevel + 1, false);
// forcing name to be defined here, might need a runtime check but it should be set if we are here
const descriptionStr = (0, write_1.getJsDocString)(settings, child.interfaceOrTypeName, childInfo.jsDoc, indentLevel);
const optionalStr = child.required ? '' : '?';
const indentString = (0, write_1.getIndentStr)(settings, indentLevel);
const modifier = child.isReadonly ? 'readonly ' : '';
return [
descriptionStr,
indentString,
modifier,
child.interfaceOrTypeName,
optionalStr,
':',
// Prevents test failures because of spaces at line endings
childInfo.tsContent.startsWith('\n') ? '' : ' ',
childInfo.tsContent,
';'
].join('');
});
objectStr = `{\n${childrenContent.join('\n')}\n${(0, write_1.getIndentStr)(settings, indentLevel - 1)}}`;
if (parsedSchema.value !== undefined && settings.supplyDefaultsInType) {
objectStr = getDefaultTypeTsContent(settings, indentLevel, parsedSchema, objectStr);
hasDefault = true;
}
}
if (doExport) {
if (hasDefault) {
return {
tsContent: `export type ${parsedSchema.interfaceOrTypeName} = ${objectStr}`,
jsDoc: parsedSchema.jsDoc
};
}
return {
tsContent: `export interface ${parsedSchema.interfaceOrTypeName} ${objectStr}`,
jsDoc: parsedSchema.jsDoc
};
}
return { tsContent: objectStr, jsDoc: parsedSchema.jsDoc };
}
default:
throw new Error(`Unsupported join operation ${parsedSchema.joinOperation}`);
}
}
function typeContentToTs(settings, parsedSchema, doExport = false) {
const { tsContent, jsDoc } = typeContentToTsHelper(settings, parsedSchema, 1, doExport);
// forcing name to be defined here, might need a runtime check but it should be set if we are here
const descriptionStr = (0, write_1.getJsDocString)(settings, parsedSchema.interfaceOrTypeName, jsDoc);
return `${descriptionStr}${tsContent}`;
}
function parseHelper(details, settings, rootSchema) {
var _a, _b, _c;
// Convert type if a valid cast type is present
if (((_a = details.flags) === null || _a === void 0 ? void 0 : _a.cast) && validCastTo.includes((_b = details.flags) === null || _b === void 0 ? void 0 : _b.cast)) {
// @NOTE - if additional values are added beyond 'string' and 'number' further transformation will
// be needed on the details object to support those types
details.type = (_c = details.flags) === null || _c === void 0 ? void 0 : _c.cast;
}
switch (details.type) {
case 'array':
return parseArray(details, settings);
case 'string':
return parseStringSchema(details, settings, rootSchema !== null && rootSchema !== void 0 ? rootSchema : false);
case 'alternatives':
return parseAlternatives(details, settings);
case 'object':
return parseObjects(details, settings);
default:
return parseBasicSchema(details, settings, rootSchema !== null && rootSchema !== void 0 ? rootSchema : false);
}
}
// TODO: will be issues with useLabels if a nested schema has a label but is not exported on its own
// TODO: will need to pass around ignoreLabels more
/**
* Parses a joi schema into a TypeContent
* @param details: the joi schema
* @param settings: settings used for parsing
* @param useLabels if true and if a schema has a label we won't parse it and instead just reference the label in the outputted type
* @param ignoreLabels a list a label to ignore if found. Sometimes nested joi schemas will inherit the parents label so we want to ignore that
* @param rootSchema
*/
function parseSchema(details, settings, useLabels = true, ignoreLabels = [], rootSchema) {
var _a;
const { interfaceOrTypeName, jsDoc, required, value, isReadonly } = getCommonDetails(details, settings);
if (interfaceOrTypeName && useLabels && !ignoreLabels.includes(interfaceOrTypeName)) {
// skip parsing and just reference the label since we assumed we parsed the schema that the label references
// TODO: do we want to use the labels description if we reference it?
let allowedValues = createAllowTypes(details, settings);
const child = (0, types_1.makeTypeContentChild)({
content: interfaceOrTypeName,
customTypes: [interfaceOrTypeName],
// If we have any allowed values, remove the jsDoc from the child as we will use it in the outer object
jsDoc: allowedValues.length > 0 ? undefined : jsDoc,
required,
isReadonly
});
if (allowedValues.length > 0) {
if (!((_a = details.flags) === null || _a === void 0 ? void 0 : _a.only)) {
allowedValues.unshift(child);
}
else {
allowedValues = [child];
}
return (0, types_1.makeTypeContentRoot)({
joinOperation: 'union',
interfaceOrTypeName: '',
children: allowedValues,
jsDoc,
required,
isReadonly
});
}
return child;
}
const baseTypes = (0, joiUtils_1.getMetadataFromDetails)('baseType', details);
if (baseTypes.length > 0) {
// If there is a baseType defined, then the user is overriding the
// type definition.
// If there are multiple base types then the deepest one will be at the
// end of the list which is most likely the one to use.
const typeToUse = baseTypes.pop();
if (settings.debug) {
// eslint-disable-next-line no-console
console.debug(`Using user-defined '${typeToUse}' for type '${details.type}'`);
}
return (0, types_1.makeTypeContentChild)({ content: typeToUse, interfaceOrTypeName, jsDoc, required, isReadonly });
}
else if (!exports.supportedJoiTypes.includes(details.type)) {
let typeToUse;
// Let's see if we can map the type to something sensible.
// If not, then set it to 'unknown'.
switch (details.type) {
case 'function':
typeToUse = '((...args: any[]) => any)';
break;
case 'symbol':
typeToUse = 'symbol';
break;
case 'binary':
typeToUse = 'Buffer';
break;
default:
typeToUse = 'unknown';
break;
}
if (settings.debug) {
// eslint-disable-next-line no-console
console.debug(`Using '${typeToUse}' for unsupported type '${details.type}'`);
}
return (0, types_1.makeTypeContentChild)({ content: typeToUse, interfaceOrTypeName, jsDoc, required, isReadonly });
}
const parsedSchema = parseHelper(details, settings, rootSchema);
if (!parsedSchema) {
return undefined;
}
parsedSchema.interfaceOrTypeName = interfaceOrTypeName;
parsedSchema.jsDoc = jsDoc;
parsedSchema.required = required;
parsedSchema.value = value;
parsedSchema.isReadonly = isReadonly;
return parsedSchema;
}
function parseBasicSchema(details, settings, rootSchema) {
var _a;
const { interfaceOrTypeName, jsDoc } = getCommonDetails(details, settings);
const joiType = details.type;
let content = joiType;
if (joiType === 'date') {
content = 'Date';
}
const values = (0, joiUtils_1.getAllowValues)(details.allow);
// at least one value
if (values.length !== 0) {
const allowedValues = createAllowTypes(details, settings);
if (values[0] === null && !((_a = details.flags) === null || _a === void 0 ? void 0 : _a.only)) {
allowedValues.unshift((0, types_1.makeTypeContentChild)({ content }));
}
return (0, types_1.makeTypeContentRoot)({ joinOperation: 'union', children: allowedValues, interfaceOrTypeName, jsDoc });
}
if (rootSchema) {
return (0, types_1.makeTypeContentRoot)({
joinOperation: 'union',
children: [(0, types_1.makeTypeContentChild)({ content, interfaceOrTypeName })],
interfaceOrTypeName,
jsDoc
});
}
else {
return (0, types_1.makeTypeContentChild)({ content, interfaceOrTypeName, jsDoc });
}
}
function createAllowTypes(details, settings) {
const values = (0, joiUtils_1.getAllowValues)(details.allow);
// at least one value
if (values.length !== 0) {
const allowedValues = values.map((value) => (0, types_1.makeTypeContentChild)({ content: typeof value === 'string' ? (0, utils_1.toStringLiteral)(value, settings.doublequoteEscape) : `${value}` }));
return allowedValues;
}
return [];
}
/**
* `undefined` is not part of this list as that would make the field optional instead
*/
const stringAllowValues = [null, ''];
function parseStringSchema(details, settings, rootSchema) {
const { interfaceOrTypeName, jsDoc } = getCommonDetails(details, settings);
const values = (0, joiUtils_1.getAllowValues)(details.allow);
// at least one value
if (values.length !== 0) {
if (values.length === 1 && values[0] === '') {
// special case of empty string sometimes used in Joi to allow just empty string
}
else {
const allowedValues = values.map(value => stringAllowValues.includes(value) && value !== ''
? (0, types_1.makeTypeContentChild)({ content: `${value}` })
: (0, types_1.makeTypeContentChild)({ content: (0, utils_1.toStringLiteral)(value, settings.doublequoteEscape) }));
if (values.filter(value => stringAllowValues.includes(value)).length === values.length) {
allowedValues.unshift((0, types_1.makeTypeContentChild)({ content: 'string' }));
}
return (0, types_1.makeTypeContentRoot)({ joinOperation: 'union', children: allowedValues, interfaceOrTypeName, jsDoc });
}
}
if (rootSchema) {
return (0, types_1.makeTypeContentRoot)({
joinOperation: 'union',
children: [(0, types_1.makeTypeContentChild)({ content: 'string', interfaceOrTypeName })],
interfaceOrTypeName,
jsDoc
});
}
else {
return (0, types_1.makeTypeContentChild)({ content: 'string', interfaceOrTypeName, jsDoc });
}
}
function parseArray(details, settings) {
var _a;
const { interfaceOrTypeName, jsDoc } = getCommonDetails(details, settings);
const isSparse = (_a = details.flags) === null || _a === void 0 ? void 0 : _a.sparse;
if (details.ordered && !details.items) {
const parsedChildren = details.ordered.map(item => parseSchema(item, settings)).filter(Boolean);
const allowedValues = createAllowTypes(details, settings);
// at least one value
if (allowedValues.length > 0) {
allowedValues.unshift((0, types_1.makeTypeContentRoot)({
joinOperation: 'tuple',
children: parsedChildren,
interfaceOrTypeName
}));
return (0, types_1.makeTypeContentRoot)({ joinOperation: 'union', children: allowedValues, interfaceOrTypeName, jsDoc });
}
return (0, types_1.makeTypeContentRoot)({
joinOperation: 'tuple',
children: parsedChildren,
interfaceOrTypeName,
jsDoc
});
}
// TODO: handle multiple things in the items arr
const item = details.items && !details.ordered ? details.items[0] : { type: 'any' };
const child = parseSchema(item, settings);
if (!child) {
return undefined;
}
const allowedValues = createAllowTypes(details, settings);
// at least one value
if (allowedValues.length !== 0) {
allowedValues.unshift((0, types_1.makeTypeContentRoot)({ joinOperation: 'list', children: [child], interfaceOrTypeName, jsDoc }));
return (0, types_1.makeTypeContentRoot)({ joinOperation: 'union', children: allowedValues, interfaceOrTypeName, jsDoc });
}
if (isSparse) {
return (0, types_1.makeTypeContentRoot)({
joinOperation: 'list',
children: [
(0, types_1.makeTypeContentRoot)({
joinOperation: 'union',
children: [child, (0, types_1.makeTypeContentChild)({ content: 'undefined' })],
interfaceOrTypeName,
jsDoc
})
],
interfaceOrTypeName,
jsDoc
});
}
return (0, types_1.makeTypeContentRoot)({ joinOperation: 'list', children: [child], interfaceOrTypeName, jsDoc });
}
function parseAlternatives(details, settings) {
const { interfaceOrTypeName, jsDoc } = getCommonDetails(details, settings);
const ignoreLabels = interfaceOrTypeName ? [interfaceOrTypeName] : [];
const children = [];
if (details.matches === undefined) {
// Edge case where the user does not pass ANY content to the `alternatives` function.
// In the official docs: If no schemas are added, the type will not match any value except for undefined.
children.push((0, types_1.makeTypeContentChild)({ content: 'undefined' }));
}
else {
children.push(...(0, utils_1.filterMap)(details.matches, match => {
// ignore alternatives().conditional() and return 'any' since we don't handle is / then / otherwise for now
if (!match.schema) {
return parseSchema({ type: 'any' }, settings, true, ignoreLabels);
}
return parseSchema(match.schema, settings, true, ignoreLabels);
}));
}
// This is a check that cannot be tested as Joi throws an error before this package
// can be called, there is test for it in alternatives
if (children.length === 0) {
/* istanbul ignore next */
return undefined;
}
const allowedValues = createAllowTypes(details, settings);
return (0, types_1.makeTypeContentRoot)({
joinOperation: 'union',
children: [...children, ...allowedValues],
interfaceOrTypeName,
jsDoc
});
}
function buildUnknownTypeContent(unknownType = 'unknown') {
return {
__isRoot: false,
content: unknownType,
interfaceOrTypeName: '[x: string]',
required: true,
jsDoc: { description: `${unknownType && unknownType[0].toUpperCase() + unknownType.slice(1)} Property` }
};
}
function parseUnknown(details, settings) {
const unknownTypes = (0, joiUtils_1.getMetadataFromDetails)('unknownType', details);
const type = unknownTypes.pop();
if (typeof type === 'string') {
return buildUnknownTypeContent(type);
}
if ((0, utils_1.isDescribe)(type)) {
const typeContent = parseSchema(type, settings);
if (!typeContent) {
// Can't think of a way to make this happen but want to keep this ready just in case
/* istanbul ignore next */
return buildUnknownTypeContent();
}
return {
...typeContent,
interfaceOrTypeName: '[x: string]',
required: true
};
}
return buildUnknownTypeContent();
}
function parsePatterns(details, settings) {
const parsedSchema = details.patterns ? parseSchema(details.patterns[0].rule, settings) : null;
if (!parsedSchema) {
return parseUnknown(details, settings);
}
parsedSchema.required = true;
parsedSchema.interfaceOrTypeName = '[pattern: string]';
return parsedSchema;
}
function parseObjects(details, settings) {
var _a, _b, _c;
const joinOperation = details.keys === undefined
? // When using Joi.object() without any argument, joi defaults to allowing ANY key/pair
// inside the object. This is reflected in the absence of the `keys` field in the `details` var.
'objectWithUndefinedKeys'
: 'object';
let children = (0, utils_1.filterMap)(Object.entries(details.keys || {}), ([key, value]) => {
const parsedSchema = parseSchema(value, settings);
// The only type that could return this is alternatives
// see parseAlternatives for why this is ignored
if (!parsedSchema) {
return undefined;
}
parsedSchema.interfaceOrTypeName = /^[$A-Z_][0-9A-Z_$]*$/i.test(key || '') ? key : `'${key}'`;
return parsedSchema;
});
if (((_a = details.patterns) === null || _a === void 0 ? void 0 : _a.length) === 1 && (((_b = details.patterns[0].schema) === null || _b === void 0 ? void 0 : _b.type) === 'string' || details.patterns[0].regex)) {
children.push(parsePatterns(details, settings));
}
if (((_c = details === null || details === void 0 ? void 0 : details.flags) === null || _c === void 0 ? void 0 : _c.unknown) === true) {
children.push(parseUnknown(details, settings));
}
if (settings.sortPropertiesByName) {
children = children.sort((a, b) => {
if (!a.interfaceOrTypeName || !b.interfaceOrTypeName) {
// interfaceOrTypeName should never be null at this point this is just in case
/* istanbul ignore next */
return 0;
}
else if (a.interfaceOrTypeName > b.interfaceOrTypeName) {
return 1;
}
else if (a.interfaceOrTypeName < b.interfaceOrTypeName) {
return -1;
}
// this next line can never happen as the object is totally invalid as the object is invalid
// the code would not build so ignoring this
/* istanbul ignore next */
return 0;
});
}
const { interfaceOrTypeName, jsDoc } = getCommonDetails(details, settings);
const allowedValues = createAllowTypes(details, settings);
// at least one value
if (allowedValues.length !== 0) {
allowedValues.unshift((0, types_1.makeTypeContentRoot)({ joinOperation, children, interfaceOrTypeName, jsDoc }));
return (0, types_1.makeTypeContentRoot)({ joinOperation: 'union', children: allowedValues, interfaceOrTypeName, jsDoc });
}
return (0, types_1.makeTypeContentRoot)({ joinOperation, children, interfaceOrTypeName, jsDoc });
}
;