From 5fcb6b45a048a1f358dffa3bf3c3d1a61fcf6273 Mon Sep 17 00:00:00 2001 From: Alisue Date: Fri, 16 Feb 2024 15:11:46 +0900 Subject: [PATCH 1/3] :herb: Add test that fails with current `isObjectOf` Prior to v1.16.0, `isObjectOf` would return `true` for an instance. --- is/factory_test.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/is/factory_test.ts b/is/factory_test.ts index 7a91a14..e44eebd 100644 --- a/is/factory_test.ts +++ b/is/factory_test.ts @@ -9,7 +9,14 @@ import { assertType } from "https://deno.land/std@0.211.0/testing/types.ts"; import { type Equal, stringify } from "./_testutil.ts"; import { type Predicate } from "./type.ts"; import { isOptionalOf } from "./annotation.ts"; -import { isArray, isBoolean, isNumber, isString, isUndefined } from "./core.ts"; +import { + isArray, + isBoolean, + isFunction, + isNumber, + isString, + isUndefined, +} from "./core.ts"; import is, { isArrayOf, isInstanceOf, @@ -612,6 +619,13 @@ Deno.test("isObjectOf", async (t) => { "Specify `{ strict: true }` and object have an unknown property", ); }); + await t.step("returns true on T instance", () => { + const date = new Date(); + const predObj = { + getFullYear: isFunction, + }; + assertEquals(isObjectOf(predObj)(date), true, "Value is not an object"); + }); await testWithExamples( t, isObjectOf({ a: (_: unknown): _ is unknown => false }), From b01d673ce6fb8cffcb413063610e0fdc76b94273 Mon Sep 17 00:00:00 2001 From: Alisue Date: Fri, 16 Feb 2024 15:20:44 +0900 Subject: [PATCH 2/3] :bug: Do NOT use `isRecord` in `isObjectOf` `isObjectOf` should support non pure object (e.g. `Date` instance) but `isRecord` check if the value is a pure object or not. --- is/factory.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/is/factory.ts b/is/factory.ts index 138de9e..392b8a1 100644 --- a/is/factory.ts +++ b/is/factory.ts @@ -511,7 +511,7 @@ export function isObjectOf< .map(([k]) => k); return setPredicateFactoryMetadata( (x: unknown): x is ObjectOf => { - if (!isRecord(x)) return false; + if (x == null || typeof x !== "object") return false; // Check required keys const s = new Set(Object.keys(x)); if (requiredKeys.some((k) => !s.has(k))) return false; From 14a83f7acb1a930fe73a1d9828808f014eaea8c4 Mon Sep 17 00:00:00 2001 From: Alisue Date: Fri, 16 Feb 2024 15:21:03 +0900 Subject: [PATCH 3/3] :bug: Do NOT check required keys in `isObjectOf` Some instance attributes are non enumerable, so we should not check. --- is/factory.ts | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/is/factory.ts b/is/factory.ts index 392b8a1..4202867 100644 --- a/is/factory.ts +++ b/is/factory.ts @@ -1,6 +1,6 @@ import type { FlatType } from "../_typeutil.ts"; import type { Predicate, PredicateType } from "./type.ts"; -import { isOptional, isOptionalOf, isReadonlyOf } from "./annotation.ts"; +import { isOptionalOf, isReadonlyOf } from "./annotation.ts"; import { isAny, isArray, @@ -506,18 +506,12 @@ export function isObjectOf< // deno-lint-ignore no-explicit-any return isStrictOf(isObjectOf(predObj)) as any; } - const requiredKeys = Object.entries(predObj) - .filter(([_, v]) => !isWithOptional(v)) - .map(([k]) => k); return setPredicateFactoryMetadata( (x: unknown): x is ObjectOf => { if (x == null || typeof x !== "object") return false; - // Check required keys - const s = new Set(Object.keys(x)); - if (requiredKeys.some((k) => !s.has(k))) return false; // Check each values for (const k in predObj) { - if (!predObj[k](x[k])) return false; + if (!predObj[k]((x as T)[k])) return false; } return true; }, @@ -529,13 +523,6 @@ type WithOptional = | WithMetadata>> | { optional: true }; // For backward compatibility -function isWithOptional>( - pred: T, -): pred is T & WithOptional { - // deno-lint-ignore no-explicit-any - return isOptional(pred) || (pred as any).optional === true; -} - type ObjectOf>> = FlatType< // Non optional & {