From 03b0949d393258cdf3316a7c1a49ca6839cb430a Mon Sep 17 00:00:00 2001 From: Endeavorance Date: Thu, 22 May 2025 14:26:11 -0400 Subject: [PATCH] Add recordOf --- package.json | 2 +- src/index.ts | 34 ++++++++++++++++++++++++++++++++++ src/test/parse.test.ts | 20 ++++++++++++++++++++ 3 files changed, 55 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index e86fa69..c2e5ad5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@endeavorance/parsec", - "version": "0.6.1", + "version": "0.7.0", "author": "Endeavorance", "type": "module", "module": "dist/index.js", diff --git a/src/index.ts b/src/index.ts index 95f824f..38da37a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -261,4 +261,38 @@ export const Parse = { throw MismatchError(shapeName, value); }; }, + + recordOf( + keyParser: Parser, + valueParser: Parser, + shapeName = "Record", + ) { + return (value: unknown): Record => { + if (value === null || typeof value !== "object") { + throw new ParseError("Record value must be an object", shapeName); + } + + for (const [key, val] of Object.entries(value)) { + try { + keyParser(key); + } catch (error) { + if (error instanceof ParseError) { + error.path += `${shapeName}["${key}"] (key)`; + } + throw error; + } + + try { + valueParser(val); + } catch (error) { + if (error instanceof ParseError) { + error.path += `${shapeName}["${key}"] (value)`; + } + throw error; + } + } + + return value as Record; + }; + }, } as const; diff --git a/src/test/parse.test.ts b/src/test/parse.test.ts index 0b7848d..c6fa439 100644 --- a/src/test/parse.test.ts +++ b/src/test/parse.test.ts @@ -319,3 +319,23 @@ describe(Parse.oneOf, () => { } }); }); + +describe(Parse.recordOf, () => { + test("Parses records of a specific type", () => { + const stringToString = Parse.recordOf(Parse.string, Parse.string); + + const rec = { + key: "value", + another: "another", + }; + + const badRec = { + key: "value", + another: 1, + }; + + expect(stringToString(rec)).toEqual(rec); + + expect(() => stringToString(badRec)).toThrow(ParseError); + }); +});