diff --git a/is/__snapshots__/required_of_test.ts.snap b/is/__snapshots__/required_of_test.ts.snap index 7c30fac..2bb6a96 100644 --- a/is/__snapshots__/required_of_test.ts.snap +++ b/is/__snapshots__/required_of_test.ts.snap @@ -23,3 +23,27 @@ snapshot[`isRequiredOf > returns properly named predicate function 2`] = ` d: asReadonly(isString) })" `; + +snapshot[`isRequiredOf > with symbol properties > returns properly named predicate function 1`] = ` +"isObjectOf({ + a: isNumber, + Symbol(b): isUnionOf([ + isString, + isUndefined + ]), + Symbol(c): isBoolean, + Symbol(d): asReadonly(isString) +})" +`; + +snapshot[`isRequiredOf > with symbol properties > returns properly named predicate function 2`] = ` +"isObjectOf({ + a: isNumber, + Symbol(b): isUnionOf([ + isString, + isUndefined + ]), + Symbol(c): isBoolean, + Symbol(d): asReadonly(isString) +})" +`; diff --git a/is/required_of.ts b/is/required_of.ts index 3b7fba7..310ba2a 100644 --- a/is/required_of.ts +++ b/is/required_of.ts @@ -42,9 +42,14 @@ export function isRequiredOf< ): & Predicate>> & IsPredObj

{ - const predObj = Object.fromEntries( - Object.entries(pred.predObj).map(([k, v]) => [k, asUnoptional(v)]), - ); + const keys = [ + ...Object.keys(pred.predObj), + ...Object.getOwnPropertySymbols(pred.predObj), + ]; + const predObj: Record> = { ...pred.predObj }; + for (const key of keys) { + predObj[key] = asUnoptional(predObj[key]); + } return isObjectOf(predObj) as & Predicate>> & IsPredObj

; diff --git a/is/required_of_test.ts b/is/required_of_test.ts index 16c00e2..3f638b4 100644 --- a/is/required_of_test.ts +++ b/is/required_of_test.ts @@ -51,4 +51,59 @@ Deno.test("isRequiredOf", async (t) => { >(true); } }); + + await t.step("with symbol properties", async (t) => { + const b = Symbol("b"); + const c = Symbol("c"); + const d = Symbol("d"); + const pred = is.ObjectOf({ + a: is.Number, + [b]: is.UnionOf([is.String, is.Undefined]), + [c]: as.Optional(is.Boolean), + [d]: as.Readonly(is.String), + }); + await t.step("returns properly named predicate function", async (t) => { + await assertSnapshot(t, isRequiredOf(pred).name); + await assertSnapshot(t, isRequiredOf(isRequiredOf(pred)).name); + }); + + await t.step("returns true on Required object", () => { + assertEquals( + isRequiredOf(pred)({ a: undefined, [b]: undefined, [c]: undefined }), + false, + "Object does not have required properties", + ); + assertEquals( + isRequiredOf(pred)({}), + false, + "Object does not have required properties", + ); + }); + + await t.step("returns false on non Required object", () => { + assertEquals(isRequiredOf(pred)("a"), false, "Value is not an object"); + assertEquals( + isRequiredOf(pred)({ a: 0, [b]: "a", [c]: "" }), + false, + "Object have a different type property", + ); + }); + + await t.step("predicated type is correct", () => { + const a: unknown = { a: 0, [b]: "a", [c]: true }; + if (isRequiredOf(pred)(a)) { + assertType< + Equal< + typeof a, + { + a: number; + [b]: string | undefined; + [c]: boolean; + readonly [d]: string; + } + > + >(true); + } + }); + }); });