sachinraja / zod-to-ts Goto Github PK
View Code? Open in Web Editor NEWgenerate TypeScript types from your Zod schema
License: MIT License
generate TypeScript types from your Zod schema
License: MIT License
It seems that adding a description to the nativeEnum()
value field outputs unknown
as shown below.
Is this a bug or expected behavior?
enum-value.mjs
import { z } from 'zod';
import { printNode, withGetType, zodToTs } from 'zod-to-ts';
const Enum = z.nativeEnum({
ONE: 1,
TWO: 2,
}).describe('Comment for Enum');
withGetType(Enum, ts => ts.factory.createIdentifier('Enum'));
const schema = z.object({
key: Enum,
});
const { node } = zodToTs(schema, { resolveNativeEnums: true });
console.log(printNode(node));
// {
// /** Comment for Enum */
// key: Enum;
// }
enum-value-with-description.mjs
import { z } from 'zod';
import { printNode, withGetType, zodToTs } from 'zod-to-ts';
const Enum = z.nativeEnum({
ONE: 1,
TWO: 2,
});
withGetType(Enum, ts => ts.factory.createIdentifier('Enum'));
const schema = z.object({
key: Enum.describe('Comment for key'),
});
const { node } = zodToTs(schema, { resolveNativeEnums: true });
console.log(printNode(node));
// {
// /** Comment for key */
// key: unknown;
// }
I use this library to generate my front typing. But I encounter a problem with certain specific cases.
If my back returns an object with dates like this:
zod.object({
firstname: zod.string(),
dateOfBirth: zod.date(),
});
The generated type will be the following :
{
firstname: string;
dateOfBirth: Date;
}
A correct typing for the out of the back but wrong for the front, the real type that the front obtains is the following :
{
firstname: string;
dateOfBirth: string; // containe Date in this string
}
(Same problem for typing back entries)
Proposed solution :
zodToTs(
zodSchema,
identifier,
[
{
type: ZodDate,
callback: (zodDate) => {
if(zodDate.def.coerce){
return zod.string() // replace a current ZodDate meets to ZodString
}
return zodDate
}
},
]
)
The operation would be simple, just before processing a type we look in the list if it has a match and if so launches the callback. the object returned from the callback will be the one that will be processed by the converter.
Thank you for your work. ๐
z.preprocess(
(a) => parseInt(z.string().parse(a), 10),
z.number().positive().max(100)
),
error:
console.log
ZodError: [
{
"code": "invalid_type",
"expected": "string",
"received": "undefined",
"path": [],
"message": "Required"
}
]
at handleResult (/Users/owner/Documents/source-code/private/express-zod-api/node_modules/zod/lib/types.js:29:23)
at ZodString.safeParse (/Users/owner/Documents/source-code/private/express-zod-api/node_modules/zod/lib/types.js:140:16)
at ZodString.parse (/Users/owner/Documents/source-code/private/express-zod-api/node_modules/zod/lib/types.js:120:29)
at Object.transform (/Users/owner/Documents/source-code/private/express-zod-api/tests/unit/client.spec.ts:19:44)
at ZodEffects._parse (/Users/owner/Documents/source-code/private/express-zod-api/node_modules/zod/lib/types.js:2261:38)
at ZodEffects._parseSync (/Users/owner/Documents/source-code/private/express-zod-api/node_modules/zod/lib/types.js:109:29)
at ZodEffects.safeParse (/Users/owner/Documents/source-code/private/express-zod-api/node_modules/zod/lib/types.js:139:29)
at ZodEffects.isOptional (/Users/owner/Documents/source-code/private/express-zod-api/node_modules/zod/lib/types.js:276:21)
at /Users/owner/Documents/source-code/private/express-zod-api/node_modules/zod-to-ts/dist/index.cjs:141:81
at Array.map (<anonymous>)
Getting this error when using with typescript 5.2.2:
"message": "Debug Failure. Unhandled SyntaxKind: Unknown.",
"stack":
Error: Debug Failure. Unhandled SyntaxKind: Unknown.
at pipelineEmitWithHintWorker (/Users/alex/dev/statsig/node_modules/.pnpm/[email protected]/node_modules/typescript/lib/typescript.js:113284:13)
at pipelineEmitWithHint (/Users/alex/dev/statsig/node_modules/.pnpm/[email protected]/node_modules/typescript/lib/typescript.js:112828:9)
at pipelineEmitWithComments (/Users/alex/dev/statsig/node_modules/.pnpm/[email protected]/node_modules/typescript/lib/typescript.js:116619:7)
at pipelineEmit (/Users/alex/dev/statsig/node_modules/.pnpm/[email protected]/node_modules/typescript/lib/typescript.js:112777:7)
at emit (/Users/alex/dev/statsig/node_modules/.pnpm/[email protected]/node_modules/typescript/lib/typescript.js:112745:7)
at emitTypeAnnotation (/Users/alex/dev/statsig/node_modules/.pnpm/[email protected]/node_modules/typescript/lib/typescript.js:115569:9)
at emitIndexSignature (/Users/alex/dev/statsig/node_modules/.pnpm/[email protected]/node_modules/typescript/lib/typescript.js:113613:7)
at pipelineEmitWithHintWorker (/Users/alex/dev/statsig/node_modules/.pnpm/[email protected]/node_modules/typescript/lib/typescript.js:112903:20)
at pipelineEmitWithHint (/Users/alex/dev/statsig/node_modules/.pnpm/[email protected]/node_modules/typescript/lib/typescript.js:112828:9)
at pipelineEmitWithComments (/Users/alex/dev/statsig/node_modules/.pnpm/[email protected]/node_modules/typescript/lib/typescript.js:116619:7)
at pipelineEmit (/Users/alex/dev/statsig/node_modules/.pnpm/[email protected]/node_modules/typescript/lib/typescript.js:112777:7)
at emit (/Users/alex/dev/statsig/node_modules/.pnpm/[email protected]/node_modules/typescript/lib/typescript.js:112745:7)
at emitListItemWithParenthesizerRule (/Users/alex/dev/statsig/node_modules/.pnpm/[email protected]/node_modules/typescript/lib/typescript.js:117065:5)
at emitNodeListItems (/Users/alex/dev/statsig/node_modules/.pnpm/[email protected]/node_modules/typescript/lib/typescript.js:115776:9)
at emitNodeList (/Users/alex/dev/statsig/node_modules/.pnpm/[email protected]/node_modules/typescript/lib/typescript.js:115715:9)
at emitList (/Users/alex/dev/statsig/node_modules/.pnpm/[email protected]/node_modules/typescript/lib/typescript.js:115673:7)
at emitTypeLiteral (/Users/alex/dev/statsig/node_modules/.pnpm/[email protected]/node_modules/typescript/lib/typescript.js:113695:7)
at pipelineEmitWithHintWorker (/Users/alex/dev/statsig/node_modules/.pnpm/[email protected]/node_modules/typescript/lib/typescript.js:112915:20)
at pipelineEmitWithHint (/Users/alex/dev/statsig/node_modules/.pnpm/[email protected]/node_modules/typescript/lib/typescript.js:112828:9)
at pipelineEmitWithComments (/Users/alex/dev/statsig/node_modules/.pnpm/[email protected]/node_modules/typescript/lib/typescript.js:116619:7)
at pipelineEmit (/Users/alex/dev/statsig/node_modules/.pnpm/[email protected]/node_modules/typescript/lib/typescript.js:112777:7)
at emit (/Users/alex/dev/statsig/node_modules/.pnpm/[email protected]/node_modules/typescript/lib/typescript.js:112745:7)
at emitListItemWithParenthesizerRule (/Users/alex/dev/statsig/node_modules/.pnpm/[email protected]/node_modules/typescript/lib/typescript.js:117065:5)
at emitNodeListItems (/Users/alex/dev/statsig/node_modules/.pnpm/[email protected]/node_modules/typescript/lib/typescript.js:115776:9)
at emitNodeList (/Users/alex/dev/statsig/node_modules/.pnpm/[email protected]/node_modules/typescript/lib/typescript.js:115715:9)
at emitList (/Users/alex/dev/statsig/node_modules/.pnpm/[email protected]/node_modules/typescript/lib/typescript.js:115673:7)
at emitUnionType (/Users/alex/dev/statsig/node_modules/.pnpm/[email protected]/node_modules/typescript/lib/typescript.js:113727:7)
at pipelineEmitWithHintWorker (/Users/alex/dev/statsig/node_modules/.pnpm/[email protected]/node_modules/typescript/lib/typescript.js:112923:20)
at pipelineEmitWithHint (/Users/alex/dev/statsig/node_modules/.pnpm/[email protected]/node_modules/typescript/lib/typescript.js:112828:9)
at pipelineEmitWithComments (/Users/alex/dev/statsig/node_modules/.pnpm/[email protected]/node_modules/typescript/lib/typescript.js:116619:7)
at pipelineEmit (/Users/alex/dev/statsig/node_modules/.pnpm/[email protected]/node_modules/typescript/lib/typescript.js:112777:7)
at print (/Users/alex/dev/statsig/node_modules/.pnpm/[email protected]/node_modules/typescript/lib/typescript.js:112690:7)
at writeNode (/Users/alex/dev/statsig/node_modules/.pnpm/[email protected]/node_modules/typescript/lib/typescript.js:112533:7)
at Object.printNode (/Users/alex/dev/statsig/node_modules/.pnpm/[email protected]/node_modules/typescript/lib/typescript.js:112497:7)
at printNode (/Users/alex/dev/statsig/node_modules/.pnpm/[email protected]_pkihvv7tkirfyj5v6rn4ddr4b4/node_modules/zod-to-ts/dist/index.cjs:42:18)
This happened when calling printNode(zodToTs(z.record(z.unknown())).node)
and other records (ex. z.record(z.string(), z.string()).optional()
)
It would be very helpful if zod-to-ts
could convert .startWith()
and .endsWidth()
to TypeScript Template Literal Types
.
For example:
import { z } from 'zod';
const fileUrlSchema = z.string().startsWith('file://');
โ โ โ
type FileUrl = `file://${string}`;
Hi - this is a great idea!
But I spent a little time confused by the README - Just getting the node
results in a typescript AST, but then the result is shown a printed source code.
I think the output in the README is more representative of calling printNode
?
If you agree I can submit a PR to fit it
When I use transform to convert the data type, I am not able to obtain the correct type.
import { z } from 'zod';
import { printNode, zodToTs } from 'zod-to-ts';
// define your Zod schema
const UserSchema = z.object({
username: z.string(),
age: z.number(),
avatar: z.string().transform((value) => {
// convert...
return {
src: value,
width: 100,
height: 100,
};
}),
});
// pass schema and name of type/identifier
const { node } = zodToTs(UserSchema, 'User');
const code = printNode(node);
console.log(code);
{
username: string;
age: number;
avatar: string;
}
Curious about the use case of this project. Zod itself is all about having types statically available at compile time. So why would one need to generate types from zod schema in the first place?
This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.
These updates have all been created already. Click a checkbox below to force a retry/rebase of any.
.github/workflows/test.yaml
actions/checkout v3
pnpm/action-setup v2
actions/setup-node v3
package.json
typescript ^4.9.4 || ^5.0.2
zod ^3
pnpm 8.15.8
Hello @sachinraja ,
After upgrading to TS 4.9, which is specified as a peer dependency of your library, I noticed the following warnings:
DeprecationWarning: 'createTypeAliasDeclaration' has been deprecated since v4.8.0. Decorators are no longer supported for this function. Callers should switch to an overload that does not accept a 'decorators' parameter.
> 56 | const inputAlias = createTypeAlias( ... );
DeprecationWarning: 'createParameterDeclaration' has been deprecated since v4.8.0. Decorators have been combined with modifiers. Callers should switch to an overload that does not accept a 'decorators' parameter.
> 49 | const response = zodToTs( ... );
DeprecationWarning: 'createIndexSignature' has been deprecated since v4.8.0. Decorators are no longer supported for this function. Callers should switch to an overload that does not accept a 'decorators' parameter.
> 49 | const response = zodToTs( ... );
Since you're operating the TS' factory methods that are subject for changes, I believe that Typescript should be the regular dependency of the package, not dev+peer. Thus, your implementation would operate the exact version of Typescript that is needed.
I'm going to create a PR for this.
Currently, this: Test: z.string().nullable().catch(null)
evaluates to Test?: any
, although it can only be string | null
. Do you see a possibility to improve this?
Reproducible example:
const TestSchema = z.object({
Test: z.string().nullable().catch(null)
});
console.log((printNode(zodToTs(TestSchema).node)));
Does anyone get this when trying to use zod-to-ts
in Deno?
deno --version
deno 1.35.3 (release, x86_64-apple-darwin)
v8 11.6.189.12
typescript 5.1.6
Maybe because of TS 5.x?
Hi!
Maybe I missed it, but I do not see any way to get the equivalent of zod.input<Type>
as a string with zodToTs. Is this something that is considered on the todo list?
Thank you a lot in advance
I would like the output to include default types, such as when you have a schema like this:
z.object({
thing: z.string().default("hello");
})
// =>
type Schema = {
/** @default "hello" */
thing: string;
}
Currently you can easily add a jsdoc comment in createTypeAlias but not for deeply nested schemas (e.g to document all properties of an interface)
index.mjs
import { z } from 'zod'
import { printNode, withGetType, zodToTs } from 'zod-to-ts'
const $enum = withGetType(z.nativeEnum({
NEG_1: -1,
POS_1: +1,
}), ts => ts.factory.createIdentifier('Enum'));
const { store } = zodToTs($enum, 'Enum', { nativeEnums: 'resolve' });
const node = store.nativeEnums[0];
console.log(printNode(node));
When I ran the above script with TypeScript v5.4, I got the following error.
Note
This error did not occur on TypeScript v5.3.
$ node index.mjs
C:\path\to\cwd\node_modules\typescript\lib\typescript.js:21074
Debug.assert(text.charCodeAt(0) !== 45 /* minus */, "Negative numbers should be created in combination with createPrefixUnaryExpression");
^
Error: Debug Failure. False expression: Negative numbers should be created in combination with createPrefixUnaryExpression
at Object.createNumericLiteral (C:\path\to\cwd\node_modules\typescript\lib\typescript.js:21074:13)
at file:///path/to/cwd/node_modules/zod-to-ts/dist/index.js:198:58
at Array.map (<anonymous>)
at zodToTsNode (file:///path/to/cwd/node_modules/zod-to-ts/dist/index.js:197:61)
at zodToTs (file:///path/to/cwd/node_modules/zod-to-ts/dist/index.js:67:16)
at file:///path/to/cwd/index.mjs:9:19
at ModuleJob.run (node:internal/modules/esm/module_job:218:25)
at async ModuleLoader.import (node:internal/modules/esm/loader:329:24)
at async loadESM (node:internal/process/esm_loader:28:7)
at async handleMainPromise (node:internal/modules/run_main:113:12)
Node.js v20.11.1
package.json
"dependencies": {
- "typescript": "5.3",
+ "typescript": "5.4",
"zod": "^3.22.4",
"zod-to-ts": "^1.2.0"
},
Is there any workaround other than continuing to use the old TypeScript?
Would be great if createTypeAlias
supported an options parameter that allows you to export the typeAlias.
Eg:
export const createTypeAlias = (node: ts.TypeNode, identifier: string, options: (string | { comment?: string; exportType?: boolean }) = {}) => {
const { comment, exportType } = typeof options === 'string' ? { comment: options, exportType: false } : options;
const modifiers = exportType ? [f.createModifier(ts.SyntaxKind.ExportKeyword)] : [];
const typeAlias = f.createTypeAliasDeclaration(
modifiers ,
f.createIdentifier(identifier),
undefined,
node,
)
if (comment) {
addJsDocComment(typeAlias, comment)
}
return typeAlias
}
Given:
import { z } from 'zod'
import { zodToTs } from 'zod-to-ts'
// define your Zod schema
const UserSchema = z.object({
username: z.string().describe("User's name"),
age: z.number().describe("User's age"),
inventory: z.object({
name: z.string().describe("Item name"),
itemId: z.number().describe("Item ID number"),
}).array(),
})
// pass schema and name of type/identifier
const { node } = zodToTs(UserSchema, 'User')
const nodeString = printNode(node, { descriptions: true })
Result:
"{
username: string // User's name
age: number // User's age
inventory: {
name: string // Item name
itemId: number // Item ID number
}[]
}"
Obviously in this case the descriptions are purely redundant but for the use cases I have in mind the comments could add real value.
Hi there! Thanks for this cool library. I'm trying to use it as a codegen tool to generate types based on BigQuery schema definitions and output them to a file.
I have a use case such as this:
const schema = z.object({
name: z.string(),
type: z.instanceof(BigQueryTimestamp),
})
const { node } = zodToTs(schema)
console.log(printNode(node))
Which results in a generated string of:
{
name: string,
type: any
}
What I would like is something which results in a generated string of:
{
name: string,
type: BigQueryTimestamp
}
And then I could manually add import { BigQueryTimestamp } from '@google-cloud/bigquery'
to the top of the output file.
Is there a way I specify the identifier for type
? Or is this something to do with when it's printed? Not super familiar with how the underlying typescript stuff works but happy to dig into it and contribute if you can point me in the right direction. Thanks!
I ran into this because I'm parsing JSON that has keys containing the dash ("-") character.
Simple example:
const simpleTest = z.object({
"needs-quotes": z.string(),
});
const testRet = ztt.zodToTs(simpleTest, "Test");
const testAlias = ztt.createTypeAlias(testRet.node, "TestType");
console.log(ztt.printNode(testAlias));
The above code will output
type TestType = {
needs-quotes: string;
};
...which isn't legal TypeScript.
Hello,
Could you please update the typescript to lastest version ?
@renovate-bot already made ร PR for it : #28
Thanks
zod already provides z.inder
, why would I need this lib?
input:
zod.object({
test: zod.coerce.string()
})
output:
{
test?: string;
}
The property is defined as possibly undefined, which is false.
Thank you for your work. ๐
In Readme it says that zod-to-ts
should be used with typescript @4
but in package json peer deeps I see "typescript": "^4.9.4 || ^5.0.2"
https://github.com/sachinraja/zod-to-ts/blob/main/package.json#LL44C14-L44C14
I propose to delete ts version from docs and use package.json
's peer deps as single source of truth if it is correct now or update it.
An after that my question is can I use this package with [email protected]
?
Currently any types with zod's .readonly()
method on them generate an any
in the output rather than matching the zod inferred type.
Using the TypeScript handbook's example:
type Shape =
| { kind: "circle"; radius: number }
| { kind: "square"; x: number }
| { kind: "triangle"; x: number; y: number };
const shape = z.discriminatedUnion("kind", [
z.object({ kind: z.literal("circle"), radius: z.number() }),
z.object({ kind: z.literal("square"), x: z.number() }),
z.object({ kind: z.literal("triangle"), x: z.number(), y: z.number() }),
]);
const { node } = zodToTs(shape, 'Shape');
const nodeString = printNode(node);
console.log("type Shape =", nodeString); // gives "type Shape = any"
Since version 3.11.4, zod supports descriptions. It would be great if that could be parsed as well and added to the TS-interface as inline comments.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.