UNPKG

zod-to-ts

Version:

generate TypeScript types from your Zod schema

366 lines (365 loc) 17.8 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.createAuxiliaryTypeStore = exports.printNode = exports.createTypeAlias = void 0; exports.zodToTs = zodToTs; const typescript_1 = __importStar(require("typescript")); const ast_helpers_1 = require("./ast-helpers.cjs"); const types_1 = require("./types.cjs"); const utils_1 = require("./utils.cjs"); function callTypeOverride(schema, options) { const getTypeOverride = options.overrides?.get(schema); if (getTypeOverride) { return getTypeOverride(typescript_1.default, options); } else if (options.overrideFunction) { return options.overrideFunction(schema, typescript_1.default, options); } else { return undefined; } } // This only applies to wrapper types function hasInnerType(def) { return 'innerType' in def; } function getDescriptionFromSchema(schema, options) { const directDescription = options.metadataRegistry?.get(schema)?.description; if (directDescription) return directDescription; if (hasInnerType(schema._zod.def)) { return options.metadataRegistry?.get(schema._zod.def.innerType)?.description; } return undefined; } function withAuxiliaryType(schema, getInner, options) { const auxiliaryTypeDefinition = { identifier: typescript_1.factory.createIdentifier(options.auxiliaryTypeStore.nextId()), }; const internalDefinitionsMap = options.auxiliaryTypeStore.definitions; internalDefinitionsMap.set(schema, auxiliaryTypeDefinition); const node = getInner(); if (auxiliaryTypeDefinition.used) { auxiliaryTypeDefinition.node = typescript_1.factory.createTypeAliasDeclaration(undefined, auxiliaryTypeDefinition.identifier, undefined, node); return typescript_1.factory.createTypeReferenceNode(auxiliaryTypeDefinition.identifier); } internalDefinitionsMap.delete(schema); return node; } function zodToTs(zod, options) { const resolvedOptions = (0, types_1.resolveOptions)(options); const node = zodToTsNode(zod, resolvedOptions); return { node }; } function zodToTsNode(schema, options) { const def = schema._zod.def; const typeOverride = callTypeOverride(schema, options); if (typeOverride) return typeOverride; const auxiliaryTypeDefinition = options.auxiliaryTypeStore.definitions.get(schema); if (auxiliaryTypeDefinition) { ; auxiliaryTypeDefinition.used = true; return typescript_1.factory.createTypeReferenceNode(auxiliaryTypeDefinition.identifier); } switch (def.type) { case 'string': { return typescript_1.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.StringKeyword); } case 'nan': case 'number': { return typescript_1.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.NumberKeyword); } case 'bigint': { return typescript_1.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.BigIntKeyword); } case 'success': case 'boolean': { return typescript_1.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.BooleanKeyword); } case 'date': { return (0, ast_helpers_1.createTypeReferenceFromString)('Date'); } case 'undefined': { return typescript_1.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.UndefinedKeyword); } case 'null': { return typescript_1.factory.createLiteralTypeNode(typescript_1.factory.createNull()); } case 'void': { return typescript_1.factory.createUnionTypeNode([ typescript_1.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.VoidKeyword), typescript_1.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.UndefinedKeyword), ]); } case 'any': { return typescript_1.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.AnyKeyword); } case 'unknown': { return typescript_1.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.UnknownKeyword); } case 'never': { return typescript_1.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.NeverKeyword); } case 'lazy': { return withAuxiliaryType(schema, () => zodToTsNode(def.getter(), options), options); } case 'literal': { // z.literal(['hi', 'bye']) -> 'hi' | 'bye' const members = def.values.map((value) => (0, utils_1.literalValueToLiteralType)(value)); if (members.length === 1) { return members[0]; } return typescript_1.factory.createUnionTypeNode(members); } case 'object': { return withAuxiliaryType(schema, () => { const properties = Object.entries(def.shape); const members = properties.map(([key, memberZodSchema]) => { const type = zodToTsNode(memberZodSchema, options); const isOptional = options.io === 'input' ? memberZodSchema._zod.optin : memberZodSchema._zod.optout; const propertySignature = typescript_1.factory.createPropertySignature(undefined, (0, ast_helpers_1.getIdentifierOrStringLiteral)(key), isOptional ? typescript_1.factory.createToken(typescript_1.default.SyntaxKind.QuestionToken) : undefined, type); const description = getDescriptionFromSchema(memberZodSchema, options); if (description) { (0, ast_helpers_1.addJsDocComment)(propertySignature, description); } return propertySignature; }); if (def.catchall) { const catchallType = zodToTsNode(def.catchall, options); members.push(typescript_1.factory.createIndexSignature(undefined, [ typescript_1.factory.createParameterDeclaration(undefined, undefined, typescript_1.factory.createIdentifier('x'), undefined, typescript_1.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.StringKeyword)), ], catchallType)); } return typescript_1.factory.createTypeLiteralNode(members); }, options); } case 'array': { const type = zodToTsNode(def.element, options); const node = typescript_1.factory.createArrayTypeNode(type); return node; } case 'enum': { // z.enum(['a', 'b', 'c']) -> 'a' | 'b' | 'c const members = Object.values(def.entries).map((value) => (0, utils_1.literalValueToLiteralType)(value)); return typescript_1.factory.createUnionTypeNode(members); } case 'union': { // z.union([z.string(), z.number()]) -> string | number const types = def.options.map((option) => zodToTsNode(option, options)); return typescript_1.factory.createUnionTypeNode(types); } case 'optional': { const innerType = zodToTsNode(def.innerType, options); return typescript_1.factory.createUnionTypeNode([ innerType, typescript_1.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.UndefinedKeyword), ]); } case 'nullable': { const innerType = zodToTsNode(def.innerType, options); return typescript_1.factory.createUnionTypeNode([ innerType, typescript_1.factory.createLiteralTypeNode(typescript_1.factory.createNull()), ]); } case 'tuple': { // z.tuple([z.string(), z.number()]) -> [string, number] const types = def.items.map((option) => zodToTsNode(option, options)); return typescript_1.factory.createTupleTypeNode(types); } case 'record': { // z.record(z.number()) -> { [x: string]: number } const keyType = zodToTsNode(def.keyType, options); const valueType = zodToTsNode(def.valueType, options); const node = typescript_1.factory.createTypeLiteralNode([ typescript_1.factory.createIndexSignature(undefined, [ typescript_1.factory.createParameterDeclaration(undefined, undefined, typescript_1.factory.createIdentifier('key'), undefined, keyType), ], valueType), ]); return node; } case 'map': { // z.map(z.string()) -> Map<string> const keyType = zodToTsNode(def.keyType, options); const valueType = zodToTsNode(def.valueType, options); const node = typescript_1.factory.createTypeReferenceNode(typescript_1.factory.createIdentifier('Map'), [ keyType, valueType, ]); return node; } case 'set': { // z.set(z.string()) -> Set<string> const type = zodToTsNode(def.valueType, options); const node = typescript_1.factory.createTypeReferenceNode(typescript_1.factory.createIdentifier('Set'), [type]); return node; } case 'intersection': { // z.number().and(z.string()) -> number & string const left = zodToTsNode(def.left, options); const right = zodToTsNode(def.right, options); const node = typescript_1.factory.createIntersectionTypeNode([left, right]); return node; } case 'promise': { // z.promise(z.string()) -> Promise<string> const type = zodToTsNode(def.innerType, options); const node = typescript_1.factory.createTypeReferenceNode(typescript_1.factory.createIdentifier('Promise'), [ type, ]); return node; } case 'function': { // z.function({ input: [z.string()], output: z.number() }) -> (args_0: string) => number const argumentSchemas = def.input._zod.def.items; const parameterTypes = argumentSchemas.map((argument, index) => { const parameterType = zodToTsNode(argument, options); return typescript_1.factory.createParameterDeclaration(undefined, undefined, typescript_1.factory.createIdentifier(`args_${index}`), undefined, parameterType); }); const returnType = zodToTsNode(def.output, options); const node = typescript_1.factory.createFunctionTypeNode(undefined, parameterTypes, returnType); return node; } case 'prefault': case 'default': { // z.string().optional().default('hi') -> string // if it's an input type, the type is optional if (options.io === 'input') { } return zodToTsNode(def.innerType, options); } case 'file': { return (0, ast_helpers_1.createTypeReferenceFromString)('File'); } case 'pipe': { const innerType = options.io === 'input' ? def.in._zod.def.type === 'transform' ? def.out : def.in : def.out; return zodToTsNode(innerType, options); } case 'readonly': { const innerType = zodToTsNode(def.innerType, options); if (typescript_1.default.isArrayTypeNode(innerType) || typescript_1.default.isTupleTypeNode(innerType)) { return typescript_1.factory.createTypeOperatorNode(typescript_1.default.SyntaxKind.ReadonlyKeyword, innerType); } if (typescript_1.default.isTypeLiteralNode(innerType)) { const members = innerType.members.map((member) => { if (typescript_1.default.isPropertySignature(member)) { return typescript_1.factory.updatePropertySignature(member, [ ...(member.modifiers ?? []), typescript_1.factory.createToken(typescript_1.default.SyntaxKind.ReadonlyKeyword), ], member.name, member.questionToken, member.type); } return member; }); return typescript_1.factory.createTypeLiteralNode(members); } if (typescript_1.default.isTypeReferenceNode(innerType) && typescript_1.default.isIdentifier(innerType.typeName)) { const identifier = innerType.typeName.text; if (identifier === 'Set') return typescript_1.factory.createTypeReferenceNode(typescript_1.factory.createIdentifier('ReadonlySet'), innerType.typeArguments); if (identifier === 'Map') return typescript_1.factory.createTypeReferenceNode(typescript_1.factory.createIdentifier('ReadonlyMap'), innerType.typeArguments); } // fall back to just returning the inner type return innerType; } case 'catch': { // z.string().catch('default') -> string return zodToTsNode(def.innerType, options); } case 'nonoptional': { // z.string().optional().nonoptional() -> Exclude<string | undefined, undefined> -> string return typescript_1.factory.createTypeReferenceNode('Exclude', [ zodToTsNode(def.innerType, options), typescript_1.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.UndefinedKeyword), ]); } case 'symbol': { return typescript_1.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.SymbolKeyword); } case 'template_literal': { const partNodes = def.parts.map((part) => { if (typeof part !== 'object' || part === null) { return (0, utils_1.literalValueToLiteralType)(part); } return zodToTsNode(part, options); }); let templateHead; const templateSpans = []; let currentTypeSpanNode; let currentMiddle = ''; for (const node of partNodes) { if (typescript_1.default.isLiteralTypeNode(node) && typescript_1.default.isStringLiteral(node.literal)) { currentMiddle += node.literal.text; continue; } if (currentTypeSpanNode) { const templateSpan = typescript_1.factory.createTemplateLiteralTypeSpan(currentTypeSpanNode, typescript_1.factory.createTemplateMiddle(currentMiddle)); templateSpans.push(templateSpan); } else { // if it's the first, set the head templateHead = typescript_1.factory.createTemplateHead(currentMiddle); currentTypeSpanNode = node; } currentMiddle = ''; currentTypeSpanNode = node; } if (templateHead && currentTypeSpanNode) { const templateSpan = typescript_1.factory.createTemplateLiteralTypeSpan(currentTypeSpanNode, typescript_1.factory.createTemplateTail(currentMiddle)); templateSpans.push(templateSpan); } else { return typescript_1.factory.createLiteralTypeNode(typescript_1.factory.createNoSubstitutionTemplateLiteral(currentMiddle)); } return typescript_1.factory.createTemplateLiteralType(templateHead, templateSpans); } } return (0, utils_1.handleUnrepresentable)(options.unrepresentable, def.type); } var ast_helpers_2 = require("./ast-helpers.cjs"); Object.defineProperty(exports, "createTypeAlias", { enumerable: true, get: function () { return ast_helpers_2.createTypeAlias; } }); Object.defineProperty(exports, "printNode", { enumerable: true, get: function () { return ast_helpers_2.printNode; } }); var utils_2 = require("./utils.cjs"); Object.defineProperty(exports, "createAuxiliaryTypeStore", { enumerable: true, get: function () { return utils_2.createAuxiliaryTypeStore; } });