Skip to content

Instantly share code, notes, and snippets.

@lauslim12
Created June 14, 2023 13:06
Show Gist options
  • Save lauslim12/61467480b34cac5d3763dc1ec198d9ef to your computer and use it in GitHub Desktop.
Save lauslim12/61467480b34cac5d3763dc1ec198d9ef to your computer and use it in GitHub Desktop.
Experiments to deeply merge nested objects in Zod library.
// mkdir zod-deep-merge-experiement
// Copy and paste this code to your editor, save as `index.ts`
// yarn init --yes
// yarn add ts-node zod typescript @types/node
// yarn run tsc --init
// yarn run ts-node .
import { strict as assert } from "node:assert";
import { z } from "zod";
/**
* All schemas
*/
const personSchema = z.object({
name: z.string(),
hobbies: z.array(z.string()),
attributes: z.record(z.string()),
company: z.object({
name: z.string(),
address: z.string(),
train: z.enum(["Yamanote", "Fukutoshin", "Tobu-Tojo", "Seibu Shinjuku"]),
}),
});
const alternativePersonSchema = z.object({
address: z.string(),
age: z.number(),
description: z.string(),
company: z.object({
profession: z.string(),
description: z.string(),
}),
});
/**
* First solution, dirty solution.
*/
const coalescedPerson = z
.object({})
.merge(personSchema)
.merge(alternativePersonSchema)
.deepPartial()
.and(personSchema.pick({ company: true }).deepPartial());
type CoalescedPerson = z.infer<typeof coalescedPerson>;
/**
* Second solution, a bit clearer because there's no `pick` and `deepPartial` in one line. We just
* create a new schema based on what is required to be deeply merged, so we can reuse this in the next step.
*/
const nestedCompany = z.object({
company: z
.object({})
.merge(personSchema.shape.company)
.merge(alternativePersonSchema.shape.company),
});
const alternativeCoalescedPerson = z
.object({})
.merge(personSchema)
.merge(alternativePersonSchema)
.merge(nestedCompany)
.deepPartial();
type AlternativeCoalescedPerson = z.infer<typeof alternativeCoalescedPerson>;
if (require.main === module) {
const T: CoalescedPerson = { company: { profession: "Building Management" } };
const K: AlternativeCoalescedPerson = { company: { train: "Yamanote" } };
assert.deepStrictEqual(T, { company: { profession: "Building Management" } });
assert.deepStrictEqual(K, { company: { train: "Yamanote" } });
console.log(T, K);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment