--- title: Use extend() for Adding Fields impact: MEDIUM-HIGH impactDescription: Merging objects manually loses type information; extend() preserves types and allows overriding fields safely tags: object, extend, composition, inheritance --- ## Use extend() for Adding Fields When building on existing schemas, use `.extend()` to add new fields rather than manually spreading. Extend preserves type information, allows overriding existing fields, and keeps the schema relationship explicit. **Incorrect (manual object spreading):** ```typescript import { z } from 'zod' const baseUserSchema = z.object({ id: z.string(), name: z.string(), }) // Manual spreading loses Zod's schema relationship const adminUserSchema = z.object({ ...baseUserSchema.shape, // Accessing internal .shape role: z.literal('admin'), permissions: z.array(z.string()), }) // Problems: // 1. If baseUserSchema changes, TypeScript might not catch issues // 2. Can't override fields easily // 3. Loses schema methods and metadata ``` **Correct (using extend):** ```typescript import { z } from 'zod' const baseUserSchema = z.object({ id: z.string(), name: z.string(), email: z.string().email(), }) // Extend to add fields const adminUserSchema = baseUserSchema.extend({ role: z.literal('admin'), permissions: z.array(z.string()), }) type AdminUser = z.infer // { // id: string; // name: string; // email: string; // role: 'admin'; // permissions: string[]; // } // Override existing fields const strictEmailSchema = baseUserSchema.extend({ email: z.string().email().endsWith('@company.com'), // Stricter validation }) ``` **Building hierarchies with extend:** ```typescript // Base entity with common fields const entitySchema = z.object({ id: z.string().uuid(), createdAt: z.date(), updatedAt: z.date(), }) // User extends entity const userSchema = entitySchema.extend({ email: z.string().email(), name: z.string(), }) // Product extends entity const productSchema = entitySchema.extend({ name: z.string(), price: z.number().positive(), sku: z.string(), }) // Order extends entity with references const orderSchema = entitySchema.extend({ userId: z.string().uuid(), items: z.array(z.object({ productId: z.string().uuid(), quantity: z.number().int().positive(), })), total: z.number().positive(), }) ``` **Combining extend with other methods:** ```typescript const baseSchema = z.object({ id: z.string(), name: z.string(), email: z.string(), }) // Create input: no id, add password const createSchema = baseSchema .omit({ id: true }) .extend({ password: z.string().min(8), }) // Update input: all optional except id const updateSchema = baseSchema .partial() .extend({ id: z.string(), // Override to make required }) ``` **Merge for combining independent schemas:** ```typescript const addressSchema = z.object({ street: z.string(), city: z.string(), }) const contactSchema = z.object({ email: z.string().email(), phone: z.string(), }) // Merge combines two schemas (both required) const customerSchema = addressSchema.merge(contactSchema) // { street: string; city: string; email: string; phone: string } ``` **When NOT to use this pattern:** - When schemas are genuinely independent (use merge or intersection) - When you need to remove fields (use omit) Reference: [Zod API - extend](https://zod.dev/api#extend)