zod-to-ts
Version:
generate TypeScript types from your Zod schema
366 lines (365 loc) • 17.8 kB
JavaScript
"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; } });