UNPKG

joi-to-typescript

Version:

Convert Joi Schemas to TypeScript interfaces

667 lines (666 loc) 31.5 kB
"use strict"; 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 }); }