Repository: fabien0102/ts-to-zod
Pull request #66 (2021-12-08 17:18:47)

fix: union properties can be optional & nullable
# Why

This PR fixes https://github.com/fabien0102/ts-to-zod/issues/65 
Feel free to refuse, my knowledge of the source code is very limited and I might not have all the perspective needed for this edit!

codecov-commenter: # [Codecov](https://codecov.io/gh/fabien0102/ts-to-zod/pull/66?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Fabien+BERNARD) Report
> Merging [#66](https://codecov.io/gh/fabien0102/ts-to-zod/pull/66?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Fabien+BERNARD) (ea35e24) into [main](https://codecov.io/gh/fabien0102/ts-to-zod/commit/e5e49615d832129cae8a23cbf5fa0b35024e15e2?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Fabien+BERNARD) (e5e4961) will **decrease** coverage by `1.23%`.
> The diff coverage is `95.59%`.

[![Impacted file tree graph](https://codecov.io/gh/fabien0102/ts-to-zod/pull/66/graphs/tree.svg?width=650&height=150&src=pr&token=W5M8UIJ59C&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Fabien+BERNARD)](https://codecov.io/gh/fabien0102/ts-to-zod/pull/66?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Fabien+BERNARD)

```diff
@@            Coverage Diff             @@
##             main      #66      +/-   ##
==========================================
- Coverage   98.49%   97.25%   -1.24%     
==========================================
  Files           8       11       +3     
  Lines         332      474     +142     
  Branches      102      157      +55     
==========================================
+ Hits          327      461     +134     
- Misses          5       10       +5     
- Partials        0        3       +3     
```


| [Impacted Files](https://codecov.io/gh/fabien0102/ts-to-zod/pull/66?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Fabien+BERNARD) | Coverage Δ | |
|---|---|---|
| [src/core/generateZodSchema.ts](https://codecov.io/gh/fabien0102/ts-to-zod/pull/66/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Fabien+BERNARD#diff-c3JjL2NvcmUvZ2VuZXJhdGVab2RTY2hlbWEudHM=) | `95.54% <91.93%> (-1.66%)` | :arrow_down: |
| [src/utils/resolveDefaultProperties.ts](https://codecov.io/gh/fabien0102/ts-to-zod/pull/66/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Fabien+BERNARD#diff-c3JjL3V0aWxzL3Jlc29sdmVEZWZhdWx0UHJvcGVydGllcy50cw==) | `95.23% <95.23%> (ø)` | |
| [src/utils/resolveModules.ts](https://codecov.io/gh/fabien0102/ts-to-zod/pull/66/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Fabien+BERNARD#diff-c3JjL3V0aWxzL3Jlc29sdmVNb2R1bGVzLnRz) | `97.87% <97.87%> (ø)` | |
| [src/core/generate.ts](https://codecov.io/gh/fabien0102/ts-to-zod/pull/66/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Fabien+BERNARD#diff-c3JjL2NvcmUvZ2VuZXJhdGUudHM=) | `100.00% <100.00%> (ø)` | |
| [src/core/jsDocTags.ts](https://codecov.io/gh/fabien0102/ts-to-zod/pull/66/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Fabien+BERNARD#diff-c3JjL2NvcmUvanNEb2NUYWdzLnRz) | `98.50% <100.00%> (-1.50%)` | :arrow_down: |
| [src/core/validateGeneratedTypes.ts](https://codecov.io/gh/fabien0102/ts-to-zod/pull/66/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Fabien+BERNARD#diff-c3JjL2NvcmUvdmFsaWRhdGVHZW5lcmF0ZWRUeXBlcy50cw==) | `97.50% <100.00%> (+0.13%)` | :arrow_up: |
| [src/utils/isNotNull.ts](https://codecov.io/gh/fabien0102/ts-to-zod/pull/66/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Fabien+BERNARD#diff-c3JjL3V0aWxzL2lzTm90TnVsbC50cw==) | `100.00% <100.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/fabien0102/ts-to-zod/pull/66?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Fabien+BERNARD).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Fabien+BERNARD)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/fabien0102/ts-to-zod/pull/66?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Fabien+BERNARD). Last update [e48c5fe...ea35e24](https://codecov.io/gh/fabien0102/ts-to-zod/pull/66?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Fabien+BERNARD). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Fabien+BERNARD).


Patch:
diff --git a/src/core/generateZodSchema.ts b/src/core/generateZodSchema.ts
index cec83e7..92f61a2 100644
--- a/src/core/generateZodSchema.ts
+++ b/src/core/generateZodSchema.ts
@@ -439,7 +439,7 @@ function buildZodPrimitive({
       return buildZodPrimitive({
         z,
         typeNode: nodes[0],
-        isOptional: false,
+        isOptional,
         isNullable: hasNull,
         jsDocTags,
         sourceFile,
@@ -462,6 +462,14 @@ function buildZodPrimitive({
         skipParseJSDoc,
       })
     );
+
+    // Handling null value outside of the union type
+    if (hasNull) {
+      zodProperties.push({
+        identifier: "nullable",
+      });
+    }
+
     return buildZodSchema(
       z,
       "union",


Test patch:
diff --git a/src/core/generateZodSchema.test.ts b/src/core/generateZodSchema.test.ts
index 1d427fc..010dafb 100644
--- a/src/core/generateZodSchema.test.ts
+++ b/src/core/generateZodSchema.test.ts
@@ -680,6 +680,45 @@ describe("generateZodSchema", () => {
     `);
   });
 
+  it("should allow nullable on optional properties", () => {
+    const source = `export interface A {
+      a?: number | null;
+    }
+    `;
+
+    expect(generate(source)).toMatchInlineSnapshot(`
+      "export const aSchema = z.object({
+          a: z.number().optional().nullable()
+      });"
+    `);
+  });
+
+  it("should allow nullable on union properties", () => {
+    const source = `export interface A {
+      a: number | string | null;
+    }
+    `;
+
+    expect(generate(source)).toMatchInlineSnapshot(`
+      "export const aSchema = z.object({
+          a: z.union([z.number(), z.string()]).nullable()
+      });"
+    `);
+  });
+
+  it("should allow nullable on optional union properties", () => {
+    const source = `export interface A {
+      a?: number | string | null;
+    }
+    `;
+
+    expect(generate(source)).toMatchInlineSnapshot(`
+      "export const aSchema = z.object({
+          a: z.union([z.number(), z.string()]).optional().nullable()
+      });"
+    `);
+  });
+
   it("should deal with @default with all types", () => {
     const source = `export interface WithDefaults {
      /**