From fced3e7c65455cd0a8def36b96bab0045702d3cf Mon Sep 17 00:00:00 2001 From: Endeavorance Date: Wed, 21 May 2025 18:55:16 -0400 Subject: [PATCH] Improve nullable, optional, and shape --- package.json | 2 +- src/index.ts | 7 +++-- src/test/parse.test.ts | 58 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 31d3f33..e86fa69 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@endeavorance/parsec", - "version": "0.6.0", + "version": "0.6.1", "author": "Endeavorance", "type": "module", "module": "dist/index.js", diff --git a/src/index.ts b/src/index.ts index a0d82ff..95f824f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -16,7 +16,10 @@ export class ParseError extends Error { const MismatchError = (expectedType: string, receivedValue: unknown) => { return new ParseError( - `Expected: ${expectedType}, Received: ${typeof receivedValue} ${JSON.stringify(receivedValue)}`, + `Type Mismatch +Expected: ${expectedType} +Received: ${typeof receivedValue} (${JSON.stringify(receivedValue)}) +`.trim(), expectedType, ); }; @@ -149,7 +152,7 @@ export const Parse = { const result = {} as { [K in keyof T]: ReturnType }; for (const key in parsers) { - const value = (val as Record)[key] ?? undefined; + const value = (val as Record)[key]; try { // biome-ignore lint/style/noNonNullAssertion: Logically guaranteed diff --git a/src/test/parse.test.ts b/src/test/parse.test.ts index 5878a2f..0b7848d 100644 --- a/src/test/parse.test.ts +++ b/src/test/parse.test.ts @@ -157,6 +157,56 @@ describe(Parse.shape, () => { }); }); + test("allows nullable entries", () => { + const parseThing = Parse.shape({ + name: Parse.string, + another: Parse.nullable(Parse.string), + }); + + expect(parseThing({ name: "hello", another: null })).toEqual({ + name: "hello", + another: null, + }); + }); + + test("nullable entries can accpet undefined when configured as such", () => { + const parseThing = Parse.shape({ + name: Parse.string, + another: Parse.nullable(Parse.string, true), + }); + + const obj = { + name: "hello", + another: undefined, + }; + + const result = parseThing(obj); + + expect(result).toEqual({ + name: "hello", + another: null, + }); + }); + + test("optional entries can accpet null when configured as such", () => { + const parseThing = Parse.shape({ + name: Parse.string, + another: Parse.optional(Parse.string, true), + }); + + const obj = { + name: "hello", + another: null, + }; + + const result = parseThing(obj); + + expect(result).toEqual({ + name: "hello", + another: undefined, + }); + }); + test("throws when a required value is missing", () => { const parseThing = Parse.shape({ name: Parse.string, @@ -195,12 +245,20 @@ describe(Parse.optional, () => { test("Parses undefined values", () => { expect(Parse.optional(Parse.string)(undefined)).toBeUndefined(); }); + + test("Parses null values when configured", () => { + expect(Parse.optional(Parse.string, true)(null)).toBeUndefined(); + }); }); describe(Parse.nullable, () => { test("Parses null values", () => { expect(Parse.nullable(Parse.string)(null)).toBeNull(); }); + + test("Parses undefined values when configured", () => { + expect(Parse.nullable(Parse.string, true)(undefined)).toBeNull(); + }); }); describe(Parse.defaulted, () => {