Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!(isObjectOf): Accept value of object with Object.assign-ed #127

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 8 additions & 9 deletions is/object_of.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
import type { Predicate } from "../type.ts";

/**
* Return a type predicate function that returns `true` if the type of `x` is `ObjectOf<T>`.
* Return a type predicate function that returns `true` if the type of `x` is/has `ObjectOf<T>`.
*
* Use {@linkcode isRecordOf} if you want to check if the type of `x` is a record of `T`.
*
Expand All @@ -34,6 +34,12 @@ import type { Predicate } from "../type.ts";
* if (isMyType(a)) {
* const _: { a: number; b: string; c?: boolean | undefined, readonly d: string } = a;
* }
*
* // It also works for value with Object.assign
* const b: unknown = Object.assign(100, { a: 0, b: "a", d: "d" });
* if (isMyType(b)) {
* const _: { a: number; b: string; c?: boolean | undefined, readonly d: string } = b;
* }
* ```
*/
export function isObjectOf<
Expand All @@ -45,7 +51,7 @@ export function isObjectOf<
].map((k) => [k, predObj[k]]);
const pred = rewriteName(
(x): x is ObjectOf<T> => {
if (!isObject(x)) return false;
if (x == null) return false;
return preds.every(([k, pred]) => pred(x[k]));
},
"isObjectOf",
Expand All @@ -54,13 +60,6 @@ export function isObjectOf<
return annotate(pred, "predObj", predObj);
}

function isObject(x: unknown): x is Record<PropertyKey, unknown> {
if (x == null) return false;
if (typeof x !== "object" && typeof x !== "function") return false;
if (Array.isArray(x)) return false;
return true;
}

type ObjectOf<T extends Record<PropertyKey, Predicate<unknown>>> = FlatType<
// Readonly/Optional
& {
Expand Down
26 changes: 22 additions & 4 deletions is/object_of_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,30 @@ Deno.test("isObjectOf<T>", async (t) => {
true,
"Undefined properties are ignored",
);
});

await t.step("returns true on value with T object assigned", () => {
const v = { a: 0, b: "a", c: true };
// Only 'null' and 'undefined' are not Object.assign-able
assertEquals(
isObjectOf(predObj)(Object.assign("string", v)),
true,
"string",
);
assertEquals(isObjectOf(predObj)(Object.assign(100, v)), true, "number");
assertEquals(isObjectOf(predObj)(Object.assign(100n, v)), true, "bigint");
assertEquals(isObjectOf(predObj)(Object.assign(true, v)), true, "boolean");
assertEquals(isObjectOf(predObj)(Object.assign([], v)), true, "array");
assertEquals(isObjectOf(predObj)(Object.assign({}, v)), true, "object");
assertEquals(
isObjectOf(predObj)(Object.assign(() => {}, v)),
true,
"function",
);
assertEquals(
isObjectOf(predObj)(
Object.assign(() => void 0, { a: 0, b: "a", c: true }),
),
isObjectOf(predObj)(Object.assign(Symbol("symbol"), v)),
true,
"Function are treated as an object",
"symbol",
);
});

Expand Down
Loading