Skip to content

Commit

Permalink
feat[isRecordObjectOf]: checks own symbol key properties
Browse files Browse the repository at this point in the history
  • Loading branch information
Milly committed Aug 9, 2024
1 parent 8c1d66c commit 27f86b1
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 1 deletion.
4 changes: 4 additions & 0 deletions is/__snapshots__/record_object_of_test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@ snapshot[`isRecordObjectOf<T> > returns properly named predicate function 2`] =
snapshot[`isRecordObjectOf<T, K> > returns properly named predicate function 1`] = `"isRecordObjectOf(isNumber, isString)"`;
snapshot[`isRecordObjectOf<T, K> > returns properly named predicate function 2`] = `"isRecordObjectOf((anonymous), isString)"`;
snapshot[`isRecordObjectOf<T, K> > with symbol properties > returns properly named predicate function 1`] = `"isRecordObjectOf(isNumber, isSymbol)"`;
snapshot[`isRecordObjectOf<T, K> > with symbol properties > returns properly named predicate function 2`] = `"isRecordObjectOf((anonymous), isSymbol)"`;
5 changes: 4 additions & 1 deletion is/record_object_of.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@ export function isRecordObjectOf<T, K extends PropertyKey = PropertyKey>(
return rewriteName(
(x: unknown): x is Record<K, T> => {
if (!isRecordObject(x)) return false;
const keys = Object.keys(x);
const keys = [
...Object.keys(x),
...Object.getOwnPropertySymbols(x),
];
for (const k of keys) {
if (!pred(x[k])) return false;
if (predKey && !predKey(k)) return false;
Expand Down
68 changes: 68 additions & 0 deletions is/record_object_of_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,21 @@ Deno.test("isRecordObjectOf<T>", async (t) => {
);
});
});

await t.step("with symbol properties", async (t) => {
const a = Symbol("a");
await t.step("returns true on T record", () => {
assertEquals(isRecordObjectOf(is.Number)({ [a]: 0 }), true);
assertEquals(isRecordObjectOf(is.String)({ [a]: "a" }), true);
assertEquals(isRecordObjectOf(is.Boolean)({ [a]: true }), true);
});

await t.step("returns false on non T record", () => {
assertEquals(isRecordObjectOf(is.String)({ [a]: 0 }), false);
assertEquals(isRecordObjectOf(is.Number)({ [a]: "a" }), false);
assertEquals(isRecordObjectOf(is.String)({ [a]: true }), false);
});
});
});

Deno.test("isRecordObjectOf<T, K>", async (t) => {
Expand Down Expand Up @@ -89,6 +104,7 @@ Deno.test("isRecordObjectOf<T, K>", async (t) => {
});

await t.step("checks only object's own properties", async (t) => {
const s = Symbol("s");
await t.step("returns true on T record", () => {
assertEquals(
isRecordObjectOf(is.Number, is.String)(
Expand All @@ -115,6 +131,58 @@ Deno.test("isRecordObjectOf<T, K>", async (t) => {
true,
"No own properties",
);
assertEquals(
isRecordObjectOf(is.String, is.String)(
Object.assign(Object.create({ [s]: "ignore" }), {/* empty */}),
),
true,
"No own properties",
);
});
});

await t.step("with symbol properties", async (t) => {
const a = Symbol("a");
await t.step("returns properly named predicate function", async (t) => {
await assertSnapshot(t, isRecordObjectOf(is.Number, is.Symbol).name);
await assertSnapshot(
t,
isRecordObjectOf((_x): _x is string => false, is.Symbol).name,
);
});

await t.step("returns true on T record", () => {
assertEquals(isRecordObjectOf(is.Number, is.Symbol)({ [a]: 0 }), true);
assertEquals(isRecordObjectOf(is.String, is.Symbol)({ [a]: "a" }), true);
assertEquals(
isRecordObjectOf(is.Boolean, is.Symbol)({ [a]: true }),
true,
);
});

await t.step("returns false on non T record", () => {
assertEquals(isRecordObjectOf(is.String, is.Symbol)({ [a]: 0 }), false);
assertEquals(isRecordObjectOf(is.Number, is.Symbol)({ [a]: "a" }), false);
assertEquals(
isRecordObjectOf(is.String, is.Symbol)({ [a]: true }),
false,
);
});

await t.step("returns false on non K record", () => {
assertEquals(isRecordObjectOf(is.Number, is.String)({ [a]: 0 }), false);
assertEquals(isRecordObjectOf(is.String, is.String)({ [a]: "a" }), false);
assertEquals(
isRecordObjectOf(is.Boolean, is.String)({ [a]: true }),
false,
);
});

await t.step("predicated type is correct", () => {
const a: unknown = { a: 0 };
if (isRecordObjectOf(is.Number, is.Symbol)(a)) {
assertType<Equal<typeof a, Record<symbol, number>>>(true);
}
});
});
});

0 comments on commit 27f86b1

Please sign in to comment.