Add transformed

This commit is contained in:
Endeavorance 2025-05-22 14:46:18 -04:00
parent 03b0949d39
commit 71a6d5c7d7
4 changed files with 52 additions and 3 deletions

View file

@ -74,6 +74,7 @@ required and will default to an generic version of the parser.
- `Parse.regex(regex: RegExp): (val: unknown) => string`
- `Parse.literal(literal: unknown): (val: unknown) => literal`
- `Parse.instanceOf<T>(classDef: Class): (val: unknown) => T`
- `Parse.recordOf<K, V>(keyParser: Parser<K>, valParser: Parser<V>): (val: unknown) => Record<K, V>`
```ts
// An array of strings
@ -117,6 +118,26 @@ const numberOrNull = Parse.nullable(Parse.number);
const optionalArrayOfStrings = Parse.optional(Parse.arrayOf(Parse.string)));
```
Both `nullable` and `optional` can be passed `true` as a second parameter
to allow parsing `null` or `undefined` into the respective empty value.
For example, if you want to allow parsing a field that may be `null` or `undefined`,
but want to return only `null` if either is parsed, you can use:
```typescript
const nullifiedVal = Parse.nullable(undefined, true); // parses to null
```
### Defaulting and Transforming
Use `Parse.defaulted()` and `Parse.transformed()` to wrap parsers and provide
a default value or value transformer
```typescript
const parsedWithDefault = Parse.defaulted(Parse.number, 42)(undefined); // 42
const asNum = Parse.transformed(Parse.string, (val) => parseInt(val, 10))("42"); // 42
```
### Extracting Types from Parsers
When composing parsers to create more complex types, you can use the `ReturnType`

View file

@ -1,6 +1,6 @@
{
"name": "@endeavorance/parsec",
"version": "0.7.0",
"version": "0.8.0",
"author": "Endeavorance",
"type": "module",
"module": "dist/index.js",

View file

@ -241,6 +241,23 @@ export const Parse = {
};
},
transformed<I, O>(
parser: (val: unknown) => I,
transformer: (val: I) => O,
shapeName = "Transformed Value",
): (val: unknown) => O {
return (val: unknown) => {
try {
return transformer(parser(val));
} catch (error) {
if (error instanceof ParseError) {
error.path += shapeName;
}
throw error;
}
};
},
oneOf<T extends Parser<unknown>[]>(
parsers: T,
shapeName = "Union",

View file

@ -1,6 +1,8 @@
import { describe, expect, test } from "bun:test";
import { Parse, ParseError } from "../index.ts";
class TestClass { }
describe(Parse.unknown, () => {
test("returns the value as provided", () => {
const val = Parse.unknown("hello");
@ -282,8 +284,6 @@ describe(Parse.nan, () => {
});
});
class TestClass { }
describe(Parse.instanceOf, () => {
test("Parses instances of a class", () => {
const testClass = new TestClass();
@ -339,3 +339,14 @@ describe(Parse.recordOf, () => {
expect(() => stringToString(badRec)).toThrow(ParseError);
});
});
describe(Parse.transformed, () => {
test("It transforms the parsed value", () => {
const parser = Parse.transformed(Parse.string, (val) =>
Number.parseInt(val, 10),
);
const parsedVal = parser("123");
expect(parsedVal).toBe(123);
});
});