From 3ed80d515e8b83f4053085b93a032eb3312446de Mon Sep 17 00:00:00 2001 From: Francois Levasseur Date: Wed, 25 Sep 2024 11:42:00 -0400 Subject: [PATCH 1/3] fix(zui): handle all literal types when transforming to typescript --- .../zui-to-typescript-next/index.test.ts | 44 +++++++++++++++- .../zui-to-typescript-next/index.ts | 6 ++- .../zui-to-typescript-next/tmp.test.ts | 52 +++++++++++++++++++ zui/tsconfig.json | 5 +- 4 files changed, 104 insertions(+), 3 deletions(-) create mode 100644 zui/src/transforms/zui-to-typescript-next/tmp.test.ts diff --git a/zui/src/transforms/zui-to-typescript-next/index.test.ts b/zui/src/transforms/zui-to-typescript-next/index.test.ts index e007d309..0b978fd3 100644 --- a/zui/src/transforms/zui-to-typescript-next/index.test.ts +++ b/zui/src/transforms/zui-to-typescript-next/index.test.ts @@ -2,7 +2,7 @@ import { describe, it, expect } from 'vitest' import { UntitledDeclarationError, toTypescript } from '.' import z from '../../z' -describe('functions', () => { +describe.concurrent('functions', () => { it('title mandatory to declare', async () => { const fn = z .function() @@ -114,6 +114,48 @@ describe('functions', () => { `) }) + it('number literals', async () => { + const code = toTypescript(z.literal(1)) + expect(code).toMatchWithoutFormatting('1') + }) + + it('boolean literals', async () => { + const code = toTypescript(z.literal(true)) + expect(code).toMatchWithoutFormatting('true') + }) + + it('undefined literals', async () => { + const typings = toTypescript(z.literal(undefined)) + expect(typings).toMatchWithoutFormatting('undefined') + }) + + it('null literals', async () => { + const typings = toTypescript(z.literal(null)) + expect(typings).toMatchWithoutFormatting('null') + }) + + it('bigint literals', async () => { + const n = BigInt(100) + const fn = () => toTypescript(z.literal(n)) + expect(fn).toThrowError() + }) + + it('non explicitly discriminated union', async () => { + const schema = z.union([ + z.object({ enabled: z.literal(true), foo: z.string() }), + z.object({ enabled: z.literal(false), bar: z.number() }), + ]) + const typings = toTypescript(schema) + expect(typings).toMatchWithoutFormatting(`{ + enabled: true; + foo: string + } | { + enabled: false; + bar: number + } + `) + }) + it('function with named args', async () => { const fn = z.function().title('fn').args(z.string().title('firstName').optional()) const typings = toTypescript(fn, { declaration: true }) diff --git a/zui/src/transforms/zui-to-typescript-next/index.ts b/zui/src/transforms/zui-to-typescript-next/index.ts index 04cf81cb..60f83c12 100644 --- a/zui/src/transforms/zui-to-typescript-next/index.ts +++ b/zui/src/transforms/zui-to-typescript-next/index.ts @@ -274,8 +274,12 @@ ${opts.join(' | ')}` return sUnwrapZod(def.getter(), newConfig) case z.ZodFirstPartyTypeKind.ZodLiteral: + if (typeof def.value === 'bigint') { + throw new Error('BigInt literals are not supported yet') + } + const value: string = typeof def.value === 'string' ? escapeString(def.value) : `${def.value}` return `${getMultilineComment(def.description)} -${escapeString((schema as z.ZodLiteral).value)}`.trim() +${value}`.trim() case z.ZodFirstPartyTypeKind.ZodEnum: const values = def.values.map(escapeString) diff --git a/zui/src/transforms/zui-to-typescript-next/tmp.test.ts b/zui/src/transforms/zui-to-typescript-next/tmp.test.ts new file mode 100644 index 00000000..6521ed4b --- /dev/null +++ b/zui/src/transforms/zui-to-typescript-next/tmp.test.ts @@ -0,0 +1,52 @@ +import { describe, it, expect } from 'vitest' +import { toTypescript } from '.' +import z from '../../z' + +describe('functions', () => { + it('string literals', async () => { + const typings = toTypescript(z.literal('Hello, world!')) + expect(typings).toMatchWithoutFormatting(`'Hello, world!'`) + }) + + it('number literals', async () => { + const code = toTypescript(z.literal(1)) + expect(code).toMatchWithoutFormatting('1') + }) + + it('boolean literals', async () => { + const code = toTypescript(z.literal(true)) + expect(code).toMatchWithoutFormatting('true') + }) + + it('undefined literals', async () => { + const typings = toTypescript(z.literal(undefined)) + expect(typings).toMatchWithoutFormatting('undefined') + }) + + it('null literals', async () => { + const typings = toTypescript(z.literal(null)) + expect(typings).toMatchWithoutFormatting('null') + }) + + it('bigint literals', async () => { + const n = BigInt(100) + const fn = () => toTypescript(z.literal(n)) + expect(fn).toThrowError() + }) + + it('non explicitly discriminated union', async () => { + const schema = z.union([ + z.object({ enabled: z.literal(true), foo: z.string() }), + z.object({ enabled: z.literal(false), bar: z.number() }), + ]) + const typings = toTypescript(schema) + expect(typings).toMatchWithoutFormatting(`{ + enabled: true; + foo: string + } | { + enabled: false; + bar: number + } + `) + }) +}) diff --git a/zui/tsconfig.json b/zui/tsconfig.json index 3e04d8b9..438af34e 100644 --- a/zui/tsconfig.json +++ b/zui/tsconfig.json @@ -20,5 +20,8 @@ "isolatedModules": true }, "exclude": ["node_modules", "dist", "*.test.ts", "**/benchmark/**/*.ts"], - "include": ["src/**/*", "vitest.d.ts"] + "include": ["src/**/*", "vitest.d.ts"], + "ts-node": { + "esm": true + } } From fb1026044356ba71ec09504f3c413380a9eeeed2 Mon Sep 17 00:00:00 2001 From: Francois Levasseur Date: Wed, 25 Sep 2024 11:55:57 -0400 Subject: [PATCH 2/3] update --- zui/src/transforms/zui-to-json-schema/parsers/literal.ts | 2 +- zui/src/z/types/literal/index.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/zui/src/transforms/zui-to-json-schema/parsers/literal.ts b/zui/src/transforms/zui-to-json-schema/parsers/literal.ts index 652f9f94..49692bb0 100644 --- a/zui/src/transforms/zui-to-json-schema/parsers/literal.ts +++ b/zui/src/transforms/zui-to-json-schema/parsers/literal.ts @@ -32,5 +32,5 @@ export function parseLiteralDef(def: ZodLiteralDef, refs: Refs): JsonSchema7Lite return { type: parsedType === 'bigint' ? 'integer' : parsedType, const: def.value, - } + } as JsonSchema7LiteralType } diff --git a/zui/src/z/types/literal/index.ts b/zui/src/z/types/literal/index.ts index ccdb617e..30c59e18 100644 --- a/zui/src/z/types/literal/index.ts +++ b/zui/src/z/types/literal/index.ts @@ -12,12 +12,12 @@ import { Primitive, } from '../index' -export interface ZodLiteralDef extends ZodTypeDef { +export interface ZodLiteralDef extends ZodTypeDef { value: T typeName: ZodFirstPartyTypeKind.ZodLiteral } -export class ZodLiteral extends ZodType> { +export class ZodLiteral extends ZodType> { _parse(input: ParseInput): ParseReturnType { if (input.data !== this._def.value) { const ctx = this._getOrReturnCtx(input) From d5ecb0b81836b65a1c3e211fb2df7a737ba8fdce Mon Sep 17 00:00:00 2001 From: Francois Levasseur Date: Wed, 25 Sep 2024 12:01:32 -0400 Subject: [PATCH 3/3] update --- zui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zui/package.json b/zui/package.json index 4461242b..49c5c01c 100644 --- a/zui/package.json +++ b/zui/package.json @@ -1,6 +1,6 @@ { "name": "@bpinternal/zui", - "version": "0.10.0", + "version": "0.10.1", "description": "A fork of Zod with additional features", "type": "module", "source": "./src/index.ts",