Skip to content

Commit 2e392ad

Browse files
authored
Merge pull request #36 from lambdalisue/improve-performance
Improve performance of some predicator functions
2 parents e46b0a1 + a254afa commit 2e392ad

File tree

3 files changed

+62
-16
lines changed

3 files changed

+62
-16
lines changed

is.ts

Lines changed: 42 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,13 @@ export function isRecord(
138138
export function isRecordOf<T>(
139139
pred: Predicate<T>,
140140
): Predicate<RecordOf<T>> {
141-
return (x: unknown): x is RecordOf<T> =>
142-
isRecord(x) && Object.values(x).every(pred);
141+
return (x: unknown): x is RecordOf<T> => {
142+
if (!isRecord(x)) return false;
143+
for (const k in x) {
144+
if (!pred(x[k])) return false;
145+
}
146+
return true;
147+
};
143148
}
144149

145150
type FlatType<T> = T extends RecordOf<unknown>
@@ -188,16 +193,35 @@ export function isObjectOf<
188193
predObj: T,
189194
options: { strict?: boolean } = {},
190195
): Predicate<ObjectOf<T>> {
191-
const preds = Object.entries(predObj);
192-
const allKeys = new Set(preds.map(([key]) => key));
193-
const requiredKeys = preds
194-
.filter(([_, pred]) => !(pred as OptionalPredicate<unknown>).optional)
195-
.map(([key]) => key);
196-
const hasKeys = options.strict
197-
? (props: string[]) => props.every((p) => allKeys.has(p))
198-
: (props: string[]) => requiredKeys.every((k) => props.includes(k));
199-
return (x: unknown): x is ObjectOf<T> =>
200-
isRecord(x) && hasKeys(Object.keys(x)) && preds.every(([k, p]) => p(x[k]));
196+
return options.strict ? isObjectOfStrict(predObj) : isObjectOfLoose(predObj);
197+
}
198+
199+
function isObjectOfLoose<
200+
T extends RecordOf<Predicate<unknown>>,
201+
>(
202+
predObj: T,
203+
): Predicate<ObjectOf<T>> {
204+
return (x: unknown): x is ObjectOf<T> => {
205+
if (!isRecord(x)) return false;
206+
for (const k in predObj) {
207+
if (!predObj[k](x[k])) return false;
208+
}
209+
return true;
210+
};
211+
}
212+
213+
function isObjectOfStrict<
214+
T extends RecordOf<Predicate<unknown>>,
215+
>(
216+
predObj: T,
217+
): Predicate<ObjectOf<T>> {
218+
const keys = new Set(Object.keys(predObj));
219+
const pred = isObjectOfLoose(predObj);
220+
return (x: unknown): x is ObjectOf<T> => {
221+
if (!pred(x)) return false;
222+
const ks = Object.keys(x);
223+
return ks.length <= keys.size && ks.every((k) => keys.has(k));
224+
};
201225
}
202226

203227
/**
@@ -366,9 +390,12 @@ export type OptionalPredicate<T> = Predicate<T | undefined> & {
366390
export function isOptionalOf<T>(
367391
pred: Predicate<T>,
368392
): OptionalPredicate<T> {
369-
return Object.assign(isOneOf([isUndefined, pred]), {
370-
optional: true as const,
371-
});
393+
return Object.assign(
394+
(x: unknown): x is Predicate<T | undefined> => isUndefined(x) || pred(x),
395+
{
396+
optional: true as const,
397+
},
398+
) as OptionalPredicate<T>;
372399
}
373400

374401
export default {

is_bench.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,15 @@ Deno.bench({
170170
}
171171
},
172172
});
173+
Deno.bench({
174+
name: "is.ObjectOf (strict)",
175+
fn: () => {
176+
const pred = is.ObjectOf(predObj, { strict: true });
177+
for (const c of cs) {
178+
pred(c);
179+
}
180+
},
181+
});
173182

174183
const isObjectOfPred = is.ObjectOf(predObj);
175184
Deno.bench({
@@ -181,6 +190,16 @@ Deno.bench({
181190
},
182191
});
183192

193+
const isObjectOfStrictPred = is.ObjectOf(predObj, { strict: true });
194+
Deno.bench({
195+
name: "is.ObjectOf (pre, strict)",
196+
fn: () => {
197+
for (const c of cs) {
198+
isObjectOfStrictPred(c);
199+
}
200+
},
201+
});
202+
184203
Deno.bench({
185204
name: "is.Function",
186205
fn: () => {

is_test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ Deno.test("isObjectOf<T>", async (t) => {
280280
});
281281
await testWithExamples(
282282
t,
283-
isObjectOf({ a: (_: unknown): _ is unknown => true }),
283+
isObjectOf({ a: (_: unknown): _ is unknown => false }),
284284
{ excludeExamples: ["record"] },
285285
);
286286
await t.step("with optional properties", async (t) => {

0 commit comments

Comments
 (0)