Skip to content

Commit b907473

Browse files
committed
feat[isRecordOf]: checks own symbol key properties
1 parent 170ff13 commit b907473

File tree

3 files changed

+67
-1
lines changed

3 files changed

+67
-1
lines changed

is/__snapshots__/record_of_test.ts.snap

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,7 @@ snapshot[`isRecordOf<T> > returns properly named predicate function 2`] = `"isRe
77
snapshot[`isRecordOf<T, K> > returns properly named predicate function 1`] = `"isRecordOf(isNumber, isString)"`;
88
99
snapshot[`isRecordOf<T, K> > returns properly named predicate function 2`] = `"isRecordOf((anonymous), isString)"`;
10+
11+
snapshot[`isRecordOf<T, K> > with symbol properties > returns properly named predicate function 1`] = `"isRecordOf(isNumber, isSymbol)"`;
12+
13+
snapshot[`isRecordOf<T, K> > with symbol properties > returns properly named predicate function 2`] = `"isRecordOf((anonymous), isSymbol)"`;

is/record_of.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,10 @@ export function isRecordOf<T, K extends PropertyKey = PropertyKey>(
3939
return rewriteName(
4040
(x: unknown): x is Record<K, T> => {
4141
if (!isRecord(x)) return false;
42-
const keys = Object.keys(x);
42+
const keys = [
43+
...Object.keys(x),
44+
...Object.getOwnPropertySymbols(x),
45+
];
4346
for (const k of keys) {
4447
if (!pred(x[k])) return false;
4548
if (predKey && !predKey(k)) return false;

is/record_of_test.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,21 @@ Deno.test("isRecordOf<T>", async (t) => {
5252
);
5353
});
5454
});
55+
56+
await t.step("with symbol properties", async (t) => {
57+
const a = Symbol("a");
58+
await t.step("returns true on T record", () => {
59+
assertEquals(isRecordOf(is.Number)({ [a]: 0 }), true);
60+
assertEquals(isRecordOf(is.String)({ [a]: "a" }), true);
61+
assertEquals(isRecordOf(is.Boolean)({ [a]: true }), true);
62+
});
63+
64+
await t.step("returns false on non T record", () => {
65+
assertEquals(isRecordOf(is.String)({ [a]: 0 }), false);
66+
assertEquals(isRecordOf(is.Number)({ [a]: "a" }), false);
67+
assertEquals(isRecordOf(is.String)({ [a]: true }), false);
68+
});
69+
});
5570
});
5671

5772
Deno.test("isRecordOf<T, K>", async (t) => {
@@ -89,6 +104,7 @@ Deno.test("isRecordOf<T, K>", async (t) => {
89104
});
90105

91106
await t.step("checks only object's own properties", async (t) => {
107+
const s = Symbol("s");
92108
await t.step("returns true on T record", () => {
93109
assertEquals(
94110
isRecordOf(is.Number, is.String)(
@@ -115,6 +131,49 @@ Deno.test("isRecordOf<T, K>", async (t) => {
115131
true,
116132
"No own properties",
117133
);
134+
assertEquals(
135+
isRecordOf(is.String, is.String)(
136+
Object.assign(Object.create({ [s]: "ignore" }), {/* empty */}),
137+
),
138+
true,
139+
"No own properties",
140+
);
141+
});
142+
});
143+
144+
await t.step("with symbol properties", async (t) => {
145+
const a = Symbol("a");
146+
await t.step("returns properly named predicate function", async (t) => {
147+
await assertSnapshot(t, isRecordOf(is.Number, is.Symbol).name);
148+
await assertSnapshot(
149+
t,
150+
isRecordOf((_x): _x is string => false, is.Symbol).name,
151+
);
152+
});
153+
154+
await t.step("returns true on T record", () => {
155+
assertEquals(isRecordOf(is.Number, is.Symbol)({ [a]: 0 }), true);
156+
assertEquals(isRecordOf(is.String, is.Symbol)({ [a]: "a" }), true);
157+
assertEquals(isRecordOf(is.Boolean, is.Symbol)({ [a]: true }), true);
158+
});
159+
160+
await t.step("returns false on non T record", () => {
161+
assertEquals(isRecordOf(is.String, is.Symbol)({ [a]: 0 }), false);
162+
assertEquals(isRecordOf(is.Number, is.Symbol)({ [a]: "a" }), false);
163+
assertEquals(isRecordOf(is.String, is.Symbol)({ [a]: true }), false);
164+
});
165+
166+
await t.step("returns false on non K record", () => {
167+
assertEquals(isRecordOf(is.Number, is.String)({ [a]: 0 }), false);
168+
assertEquals(isRecordOf(is.String, is.String)({ [a]: "a" }), false);
169+
assertEquals(isRecordOf(is.Boolean, is.String)({ [a]: true }), false);
170+
});
171+
172+
await t.step("predicated type is correct", () => {
173+
const a: unknown = { a: 0 };
174+
if (isRecordOf(is.Number, is.Symbol)(a)) {
175+
assertType<Equal<typeof a, Record<symbol, number>>>(true);
176+
}
118177
});
119178
});
120179
});

0 commit comments

Comments
 (0)