Skip to content

Commit 5d02a20

Browse files
refactor(lib): Drop Zod v3 and generate v4 schemas #168
1 parent 52ada65 commit 5d02a20

5 files changed

Lines changed: 23 additions & 12 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Changed
1111

1212
- Move to xUnit v3 (#162)
13+
- Generate Zod v4 schemas (#168)
1314

1415
## [0.17.4.1] - 2025-09-19
1516

TypeContractor.Tests/TypeScript/TypeScriptWriterTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ public void Writes_Zod_Schema_With_Enum_Reference()
298298
.And.Contain("import { z } from 'zod';")
299299
.And.Contain("import { ObsoleteEnum } from './ObsoleteEnum';")
300300
.And.Contain("export const TypeWithEnumSchema = z.object({")
301-
.And.Contain(" status: z.nativeEnum(ObsoleteEnum),")
301+
.And.Contain(" status: z.enum(ObsoleteEnum),")
302302
.And.Contain("});");
303303
}
304304

@@ -318,7 +318,7 @@ public void Writes_Zod_Schema_With_Nullable_Enum()
318318
.And.Contain("import { z } from 'zod';")
319319
.And.Contain("import { ObsoleteEnum } from './ObsoleteEnum';")
320320
.And.Contain("export const TypeWithNullableEnumSchema = z.object({")
321-
.And.Contain(" status: z.nativeEnum(ObsoleteEnum).nullable(),")
321+
.And.Contain(" status: z.enum(ObsoleteEnum).nullable(),")
322322
.And.Contain("});");
323323
}
324324

TypeContractor.sln

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TypeContractor.Tool", "Type
2525
EndProject
2626
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TypeContractor.Annotations", "TypeContractor.Annotations\TypeContractor.Annotations.csproj", "{8A6455DF-CE2F-44F7-B1BB-2C1640E1AEC3}"
2727
EndProject
28+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
29+
ProjectSection(SolutionItems) = preProject
30+
tools\response.d.ts = tools\response.d.ts
31+
tools\response.ts = tools\response.ts
32+
tools\zod-errors.ts = tools\zod-errors.ts
33+
EndProjectSection
34+
EndProject
2835
Global
2936
GlobalSection(SolutionConfigurationPlatforms) = preSolution
3037
Debug|Any CPU = Debug|Any CPU
@@ -59,6 +66,9 @@ Global
5966
GlobalSection(SolutionProperties) = preSolution
6067
HideSolutionNode = FALSE
6168
EndGlobalSection
69+
GlobalSection(NestedProjects) = preSolution
70+
{02EA681E-C7D8-13C7-8484-4AC65E1B71E8} = {70D3E515-AB31-4EDB-886A-23ECD377E3AE}
71+
EndGlobalSection
6272
GlobalSection(ExtensibilityGlobals) = postSolution
6373
SolutionGuid = {BE873438-37D1-44EF-98BD-2A263351F7F2}
6474
EndGlobalSection

TypeContractor/TypeScript/ZodSchemaWriter.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,12 +69,12 @@ private static string GetImportType(Type? innerSourceType, Type sourceType)
6969
private static string? GetZodOutputType(OutputProperty property, IEnumerable<OutputType> allTypes)
7070
{
7171
if (!property.IsBuiltin && property.SourceType.IsEnum)
72-
return $"z.nativeEnum({property.SourceType.Name})";
72+
return $"z.enum({property.SourceType.Name})";
7373
else if (!property.IsBuiltin && property.IsNullable && property.SourceType.IsGenericType)
7474
{
7575
var sourceType = TypeChecks.GetGenericType(property.SourceType);
7676
if (sourceType.IsEnum)
77-
return $"z.nativeEnum({sourceType.Name}).nullable()";
77+
return $"z.enum({sourceType.Name}).nullable()";
7878
}
7979

8080
string? output;
@@ -124,17 +124,17 @@ private static string GetImportType(Type? innerSourceType, Type sourceType)
124124
if (IsOfType(sourceType, typeof(string)))
125125
output = "z.string()";
126126
else if (IsOfType(sourceType, typeof(Guid)))
127-
output = "z.string().uuid()";
127+
output = "z.guid()";
128128
else if (IsOfType(sourceType, typeof(int), typeof(long), typeof(float), typeof(double), typeof(short), typeof(byte), typeof(decimal)))
129129
output = "z.number()";
130130
else if (IsOfType(sourceType, typeof(bool)))
131131
output = "z.boolean()";
132132
else if (IsOfType(sourceType, typeof(DateTime), typeof(DateTimeOffset)))
133-
output = "z.string().datetime({ offset: true })";
133+
output = "z.iso.datetime({ offset: true })";
134134
else if (IsOfType(sourceType, typeof(DateOnly)))
135-
output = "z.string().date()";
135+
output = "z.iso.date()";
136136
else if (IsOfType(sourceType, typeof(TimeOnly)))
137-
output = "z.string().time()";
137+
output = "z.iso.time()";
138138
else if (IsOfType(sourceType, typeof(TimeSpan)))
139139
output = "z.string()"; // FIXME: Can assume some formatting here
140140
else if (TypeChecks.ImplementsIEnumerable(sourceType))

tools/zod-errors.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ const leftPad = (stringToPad: string, number: number = 1): string => {
33
};
44

55
export type ErrorRoot = {
6-
_errors: string[];
6+
errors: string[];
77
};
88
export type ErrorInstance = ErrorRoot & {
99
[key: string]: ErrorInstance;
@@ -15,13 +15,13 @@ export function formatErrors(input: ErrorRoot): any {
1515
const activeBreadcumb: string[] = [];
1616

1717
function formatErrorsRecursive(input: any, activeBreadcumb: string[]) {
18-
if (input['_errors'] && input['_errors'].length > 0) {
19-
const errorsString = `- ${input['_errors'].join('\n')}\n`;
18+
if (input['errors'] && input['errors'].length > 0) {
19+
const errorsString = `- ${input['errors'].join('\n')}\n`;
2020
result += leftPad(errorsString, activeBreadcumb.length);
2121
}
2222

2323
for (const key in input) {
24-
if (key === '_errors') continue;
24+
if (key === 'errors') continue;
2525

2626
result += `${leftPad(key, activeBreadcumb.length)}:\n`;
2727

0 commit comments

Comments
 (0)