Skip to content

Commit a7a757b

Browse files
committed
feat[isPartialOf]: discover symbol properties in pred.predObj
1 parent 1848cb6 commit a7a757b

File tree

3 files changed

+83
-3
lines changed

3 files changed

+83
-3
lines changed

is/__snapshots__/partial_of_test.ts.snap

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,27 @@ snapshot[`isPartialOf<T> > returns properly named predicate function 2`] = `
2323
d: asOptional(asReadonly(isString))
2424
})"
2525
`;
26+
27+
snapshot[`isPartialOf<T> > with symbol properties > returns properly named predicate function 1`] = `
28+
"isObjectOf({
29+
a: asOptional(isNumber),
30+
Symbol(b): asOptional(isUnionOf([
31+
isString,
32+
isUndefined
33+
])),
34+
Symbol(c): asOptional(isBoolean),
35+
Symbol(c): asOptional(asReadonly(isString))
36+
})"
37+
`;
38+
39+
snapshot[`isPartialOf<T> > with symbol properties > returns properly named predicate function 2`] = `
40+
"isObjectOf({
41+
a: asOptional(isNumber),
42+
Symbol(b): asOptional(isUnionOf([
43+
isString,
44+
isUndefined
45+
])),
46+
Symbol(c): asOptional(isBoolean),
47+
Symbol(c): asOptional(asReadonly(isString))
48+
})"
49+
`;

is/partial_of.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,14 @@ export function isPartialOf<
4242
):
4343
& Predicate<FlatType<Partial<T>>>
4444
& IsPredObj<P> {
45-
const predObj = Object.fromEntries(
46-
Object.entries(pred.predObj).map(([k, v]) => [k, asOptional(v)]),
47-
) as Record<PropertyKey, Predicate<unknown>>;
45+
const keys = [
46+
...Object.keys(pred.predObj),
47+
...Object.getOwnPropertySymbols(pred.predObj),
48+
];
49+
const predObj: Record<PropertyKey, Predicate<unknown>> = { ...pred.predObj };
50+
for (const key of keys) {
51+
predObj[key] = asOptional(predObj[key]);
52+
}
4853
return isObjectOf(predObj) as
4954
& Predicate<FlatType<Partial<T>>>
5055
& IsPredObj<P>;

is/partial_of_test.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,55 @@ Deno.test("isPartialOf<T>", async (t) => {
4747
>(true);
4848
}
4949
});
50+
51+
await t.step("with symbol properties", async (t) => {
52+
const b = Symbol("b");
53+
const c = Symbol("c");
54+
const d = Symbol("c");
55+
const pred = is.ObjectOf({
56+
a: is.Number,
57+
[b]: is.UnionOf([is.String, is.Undefined]),
58+
[c]: as.Optional(is.Boolean),
59+
[d]: as.Readonly(is.String),
60+
});
61+
62+
await t.step("returns properly named predicate function", async (t) => {
63+
await assertSnapshot(t, isPartialOf(pred).name);
64+
await assertSnapshot(t, isPartialOf(isPartialOf(pred)).name);
65+
});
66+
67+
await t.step("returns true on Partial<T> object", () => {
68+
assertEquals(
69+
isPartialOf(pred)({ a: undefined, [b]: undefined, [c]: undefined }),
70+
true,
71+
);
72+
assertEquals(isPartialOf(pred)({}), true);
73+
});
74+
75+
await t.step("returns false on non Partial<T> object", () => {
76+
assertEquals(isPartialOf(pred)("a"), false, "Value is not an object");
77+
assertEquals(
78+
isPartialOf(pred)({ a: 0, [b]: "a", [c]: "" }),
79+
false,
80+
"Object have a different type property",
81+
);
82+
});
83+
84+
await t.step("predicated type is correct", () => {
85+
const a: unknown = { a: 0, [b]: "a", [c]: true };
86+
if (isPartialOf(pred)(a)) {
87+
assertType<
88+
Equal<
89+
typeof a,
90+
Partial<{
91+
a: number;
92+
[b]: string;
93+
[c]: boolean;
94+
readonly [d]: string;
95+
}>
96+
>
97+
>(true);
98+
}
99+
});
100+
});
50101
});

0 commit comments

Comments
 (0)