Skip to content

Commit 3e76fa8

Browse files
committed
feat[isObjectOf]: discover symbol properties in predObj
Ref #94, #95
1 parent f1c49f5 commit 3e76fa8

File tree

2 files changed

+108
-1
lines changed

2 files changed

+108
-1
lines changed

is/object_of.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,10 @@ export function isObjectOf<
4848
Array.isArray(x)
4949
) return false;
5050
// Check each values
51-
return Object.keys(predObj).every((k) => predObj[k]((x as T)[k]));
51+
return [
52+
...Object.keys(predObj),
53+
...Object.getOwnPropertySymbols(predObj),
54+
].every((k) => predObj[k]((x as T)[k]));
5255
},
5356
"isObjectOf",
5457
predObj,

is/object_of_test.ts

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,4 +152,108 @@ Deno.test("isObjectOf<T>", async (t) => {
152152
});
153153
},
154154
);
155+
156+
await t.step("with symbol properties", async (t) => {
157+
const s = Symbol("s");
158+
const predObj = {
159+
a: is.Number,
160+
b: is.String,
161+
[s]: is.Boolean,
162+
};
163+
164+
await t.step("returns true on T object", () => {
165+
assertEquals(isObjectOf(predObj)({ a: 0, b: "a", [s]: true }), true);
166+
assertEquals(
167+
isObjectOf(predObj)({ a: 0, b: "a", [s]: true, d: "ignored" }),
168+
true,
169+
"Undefined properties are ignored",
170+
);
171+
assertEquals(
172+
isObjectOf(predObj)({
173+
a: 0,
174+
b: "a",
175+
[s]: true,
176+
[Symbol("t")]: "ignored",
177+
}),
178+
true,
179+
"Undefined symbol properties are ignored",
180+
);
181+
assertEquals(
182+
isObjectOf(predObj)(
183+
Object.assign(() => void 0, { a: 0, b: "a", [s]: true }),
184+
),
185+
true,
186+
"Function are treated as an object",
187+
);
188+
});
189+
190+
await t.step("returns false on non T object", () => {
191+
assertEquals(isObjectOf(predObj)("a"), false, "Value is not an object");
192+
assertEquals(
193+
isObjectOf(predObj)({ a: 0, b: "a", [s]: "" }),
194+
false,
195+
"Object have a different type symbol property",
196+
);
197+
assertEquals(
198+
isObjectOf(predObj)({ a: 0, b: "a" }),
199+
false,
200+
"Object does not have symbol property",
201+
);
202+
const arrayWithSymbolProp = ["ignored"];
203+
// deno-lint-ignore no-explicit-any
204+
(arrayWithSymbolProp as any)[s] = true;
205+
assertEquals(
206+
isObjectOf({ [s]: is.Boolean })(arrayWithSymbolProp),
207+
false,
208+
"Value is not an object",
209+
);
210+
});
211+
212+
await t.step("predicated type is correct", () => {
213+
const a = Symbol("a");
214+
const b = Symbol("b");
215+
const c = Symbol("c");
216+
const d = Symbol("d");
217+
const e = Symbol("e");
218+
const f = Symbol("f");
219+
const predObj2 = {
220+
[a]: as.Readonly(as.Optional(is.String)),
221+
[b]: as.Optional(as.Readonly(is.String)),
222+
[c]: as.Readonly(is.String),
223+
[d]: as.Optional(is.String),
224+
[e]: as.Unreadonly(as.Unoptional(as.Readonly(as.Optional(is.String)))),
225+
[f]: as.Unoptional(as.Unreadonly(as.Optional(as.Readonly(is.String)))),
226+
};
227+
const x: unknown = {};
228+
229+
if (isObjectOf(predObj)(x)) {
230+
assertType<
231+
Equal<
232+
typeof x,
233+
{
234+
a: number;
235+
b: string;
236+
[s]: boolean;
237+
}
238+
>
239+
>(true);
240+
}
241+
242+
if (isObjectOf(predObj2)(x)) {
243+
assertType<
244+
Equal<
245+
typeof x,
246+
{
247+
readonly [a]?: string;
248+
readonly [b]?: string;
249+
readonly [c]: string;
250+
[d]?: string;
251+
[e]: string;
252+
[f]: string;
253+
}
254+
>
255+
>(true);
256+
}
257+
});
258+
});
155259
});

0 commit comments

Comments
 (0)