Skip to content

Commit f12da7f

Browse files
committed
feat[isStrictOf]: discover symbol properties in pred.predObj
1 parent 0f9904a commit f12da7f

File tree

3 files changed

+131
-4
lines changed

3 files changed

+131
-4
lines changed

is/__snapshots__/strict_of_test.ts.snap

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,19 @@ snapshot[`isStrictOf<T> > returns properly named predicate function 3`] = `
1717
}))
1818
}))"
1919
`;
20+
21+
snapshot[`isStrictOf<T> > with symbol properties > returns properly named predicate function 1`] = `
22+
"isStrictOf(isObjectOf({
23+
a: isNumber,
24+
Symbol(b): isString,
25+
Symbol(c): isBoolean
26+
}))"
27+
`;
28+
29+
snapshot[`isStrictOf<T> > with symbol properties > returns properly named predicate function 2`] = `
30+
"isStrictOf(isObjectOf({
31+
a: isStrictOf(isObjectOf({
32+
Symbol(b): isStrictOf(isObjectOf({Symbol(c): isBoolean}))
33+
}))
34+
}))"
35+
`;

is/strict_of.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,18 @@ export function isStrictOf<
4040
):
4141
& Predicate<T>
4242
& IsPredObj<P> {
43-
const s = new Set(Object.keys(pred.predObj));
43+
const s = new Set(getKeys(pred.predObj));
4444
return rewriteName(
4545
(x: unknown): x is T => {
4646
if (!pred(x)) return false;
47-
// deno-lint-ignore no-explicit-any
48-
const ks = Object.keys(x as any);
49-
return ks.length <= s.size && ks.every((k) => s.has(k));
47+
const ks = new Set(getKeys(x));
48+
return ks.difference(s).size === 0;
5049
},
5150
"isStrictOf",
5251
pred,
5352
) as Predicate<T> & IsPredObj<P>;
5453
}
54+
55+
function getKeys(o: Record<PropertyKey, unknown>) {
56+
return [...Object.keys(o), ...Object.getOwnPropertySymbols(o)];
57+
}

is/strict_of_test.ts

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,4 +155,112 @@ Deno.test("isStrictOf<T>", async (t) => {
155155
}
156156
});
157157
});
158+
159+
await t.step("with symbol properties", async (t) => {
160+
const b = Symbol("b");
161+
const c = Symbol("c");
162+
const predObj = {
163+
a: is.Number,
164+
[b]: is.UnionOf([is.String, is.Undefined]),
165+
[c]: as.Optional(is.Boolean),
166+
};
167+
await t.step("returns properly named predicate function", async (t) => {
168+
await assertSnapshot(
169+
t,
170+
isStrictOf(
171+
is.ObjectOf({ a: is.Number, [b]: is.String, [c]: is.Boolean }),
172+
)
173+
.name,
174+
);
175+
await assertSnapshot(
176+
t,
177+
isStrictOf(
178+
is.ObjectOf({
179+
a: isStrictOf(
180+
is.ObjectOf({
181+
[b]: isStrictOf(is.ObjectOf({ [c]: is.Boolean })),
182+
}),
183+
),
184+
}),
185+
).name,
186+
);
187+
});
188+
189+
await t.step("returns true on T object", () => {
190+
assertEquals(
191+
isStrictOf(is.ObjectOf(predObj))({ a: 0, [b]: "a", [c]: true }),
192+
true,
193+
);
194+
assertEquals(
195+
isStrictOf(is.ObjectOf(predObj))({ a: 0, [b]: "a" }),
196+
true,
197+
"Object does not have an optional property",
198+
);
199+
assertEquals(
200+
isStrictOf(is.ObjectOf(predObj))({ a: 0, [b]: "a", [c]: undefined }),
201+
true,
202+
"Object has `undefined` as value of optional property",
203+
);
204+
});
205+
206+
await t.step("returns false on non T object", () => {
207+
assertEquals(
208+
isStrictOf(is.ObjectOf(predObj))({ a: 0, [b]: "a", [c]: "" }),
209+
false,
210+
"Object have a different type property",
211+
);
212+
assertEquals(
213+
isStrictOf(is.ObjectOf(predObj))({ a: 0, [b]: "a", [c]: null }),
214+
false,
215+
"Object has `null` as value of optional property",
216+
);
217+
assertEquals(
218+
isStrictOf(is.ObjectOf(predObj))({
219+
a: 0,
220+
[b]: "a",
221+
[c]: true,
222+
d: "invalid",
223+
}),
224+
false,
225+
"Object have an unknown property",
226+
);
227+
assertEquals(
228+
isStrictOf(is.ObjectOf(predObj))({
229+
a: 0,
230+
[b]: "a",
231+
[c]: true,
232+
[Symbol("d")]: "invalid",
233+
}),
234+
false,
235+
"Object have an unknown symbol property",
236+
);
237+
assertEquals(
238+
isStrictOf(is.ObjectOf(predObj))({
239+
a: 0,
240+
[b]: "a",
241+
d: "invalid",
242+
}),
243+
false,
244+
"Object have the same number of properties but an unknown property exists",
245+
);
246+
assertEquals(
247+
isStrictOf(is.ObjectOf(predObj))({
248+
a: 0,
249+
[b]: "a",
250+
[Symbol("d")]: "invalid",
251+
}),
252+
false,
253+
"Object have the same number of properties but an unknown symbol property exists",
254+
);
255+
});
256+
257+
await t.step("predicated type is correct", () => {
258+
const a: unknown = { a: 0, [b]: "a" };
259+
if (isStrictOf(is.ObjectOf(predObj))(a)) {
260+
assertType<
261+
Equal<typeof a, { a: number; [b]: string | undefined; [c]?: boolean }>
262+
>(true);
263+
}
264+
});
265+
});
158266
});

0 commit comments

Comments
 (0)