From 0170e3c9467dec5ce53d03fc8ee0edeec40bd02e Mon Sep 17 00:00:00 2001 From: Colin McDonnell Date: Sun, 27 Sep 2020 15:59:05 -0700 Subject: [PATCH] Fixed explicit undefined values for optional keys. Fixes #161 --- README.md | 1 + src/__tests__/object.test.ts | 13 +++++++++++++ src/parser.ts | 21 ++++++++++++++++++-- src/playground.ts | 37 ++++++++++++++++++++++++------------ 4 files changed, 58 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 5833cb688..fa2e8a48d 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,7 @@ Some other great aspects: - Immutability: methods (i.e. `.optional()` return a new instance - Concise, chainable interface - Functional approach: [parse, don't validate](https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/) +- Works with plain JavaScript too! You don't need to use TypeScript. # Sponsorship diff --git a/src/__tests__/object.test.ts b/src/__tests__/object.test.ts index 4b21dee1b..cfe8c4234 100644 --- a/src/__tests__/object.test.ts +++ b/src/__tests__/object.test.ts @@ -236,3 +236,16 @@ test('catchall overrides strict', () => { asdf: 1234, }); }); + +test('test that optional keys are unset', async () => { + const SNamedEntity = z.object({ + id: z.string(), + set: z.string().optional(), + unset: z.string().optional(), + }); + const result = await SNamedEntity.parse({ + id: 'asdf', + set: undefined, + }); + expect(Object.keys(result)).toEqual(['id', 'set']); +}); diff --git a/src/parser.ts b/src/parser.ts index a8df449b7..fb30bd7b7 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -367,19 +367,36 @@ export const ZodParser = (schema: z.ZodType) => ( for (const key of shapeKeys) { const keyValidator = shapeKeys.includes(key) - ? def.shape()[key] + ? shape[key] : !(def.catchall instanceof ZodNever) ? def.catchall : undefined; if (!keyValidator) continue; + // check if schema and value are both optional + try { + keyValidator.parse(undefined, { + ...params, + path: [...params.path, key], + }); + + // const keyDataType = getParsedType(data[key]); + if (!Object.keys(data).includes(key)) { + // schema is optional + // data is undefined + // don't explicity add undefined to outut + continue; + } + } catch (err) {} + objectPromises[key] = new PseudoPromise().then(() => { try { - return keyValidator.parse(data[key], { + const parsedValue = keyValidator.parse(data[key], { ...params, path: [...params.path, key], }); + return parsedValue; } catch (err) { if (err instanceof ZodError) { const zerr: ZodError = err; diff --git a/src/playground.ts b/src/playground.ts index 2ae1e30b0..99994bd8f 100644 --- a/src/playground.ts +++ b/src/playground.ts @@ -1,17 +1,30 @@ import * as z from '.'; -// import { Scalars } from './helpers/primitive'; -const obj = z.object({ - primitiveTuple: z.tuple([z.string(), z.number()]), - nonprimitiveTuple: z.tuple([z.string(), z.number().array()]), -}); +const run = async () => { + const SNamedEntity = z.object({ + id: z.string(), + set: z.string().optional(), + unset: z.string().optional(), + }); + const result = await SNamedEntity.parse({ + id: 'asdf', + set: undefined, + }); + console.log(result); + console.log(Object.keys(result)); +}; +run(); -type obj = z.infer; +// export const T = z.object({ +// test: z.string().optional(), +// }); -const prim = obj.primitives(); -console.log(prim.shape); -const nonprim = obj.nonprimitives(); -console.log(nonprim.shape); -// .primitives(); +// console.log(T.safeParse({})); -// type t1 = [[string, number]] extends [Scalars] ? true : false; +// const r = T.safeParse({}); + +// if (r.success) { +// console.log(JSON.stringify(r.data)); +// } + +// console.log(JSON.stringify({ test: undefined, test2: undefined }, null, 2));