Skip to content
Merged
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
2 changes: 2 additions & 0 deletions _testutil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ export type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends
export function stringify(x: unknown): string {
if (x instanceof Date) return `Date(${x.valueOf()})`;
if (x instanceof Promise) return "Promise";
if (x instanceof Set) return `Set(${stringify([...x.values()])})`;
if (x instanceof Map) return `Map(${stringify([...x.entries()])})`;
if (typeof x === "function") return x.toString();
if (typeof x === "bigint") return `${x}n`;
if (typeof x === "symbol") return x.toString();
Expand Down
23 changes: 22 additions & 1 deletion as/optional_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { assertEquals } from "@std/assert";
import { assertType } from "@std/testing/types";
import { type Equal, testWithExamples } from "../_testutil.ts";
import { is } from "../is/mod.ts";
import { asOptional, asUnoptional } from "./optional.ts";
import type { AsOptional } from "../_annotation.ts";
import { asOptional, asUnoptional, hasOptional } from "./optional.ts";

Deno.test("asOptional<T>", async (t) => {
await t.step("returns a property named predicate function", () => {
Expand Down Expand Up @@ -153,3 +154,23 @@ Deno.test("asUnoptional<T>", async (t) => {
});
});
});

Deno.test("hasOptional<P>", async (t) => {
await t.step("returns true on AsOptional<T> predicate", () => {
const pred = asOptional(is.Number);
assertEquals(hasOptional(pred), true);
});

await t.step("returns true on non AsOptional<T> predicate", () => {
const pred = is.Number;
assertEquals(hasOptional(pred), false);
});

await t.step("predicated type is correct", () => {
const pred = asOptional(is.Number);
type P = typeof pred;
if (hasOptional(pred)) {
assertType<Equal<typeof pred, P & AsOptional<number>>>(true);
}
});
});
23 changes: 22 additions & 1 deletion as/readonly_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { assertEquals } from "@std/assert";
import { assertType } from "@std/testing/types";
import { type Equal, testWithExamples } from "../_testutil.ts";
import { is } from "../is/mod.ts";
import { asReadonly, asUnreadonly } from "./readonly.ts";
import type { AsReadonly } from "../_annotation.ts";
import { asReadonly, asUnreadonly, hasReadonly } from "./readonly.ts";

Deno.test("asReadonly<T>", async (t) => {
await t.step("returns a property named predicate function", () => {
Expand Down Expand Up @@ -155,3 +156,23 @@ Deno.test("asUnreadonly<T>", async (t) => {
});
});
});

Deno.test("hasReadonly<P>", async (t) => {
await t.step("returns true on AsReadonly<T> predicate", () => {
const pred = asReadonly(is.Number);
assertEquals(hasReadonly(pred), true);
});

await t.step("returns true on non AsReadonly<T> predicate", () => {
const pred = is.Number;
assertEquals(hasReadonly(pred), false);
});

await t.step("predicated type is correct", () => {
const pred = asReadonly(is.Number);
type P = typeof pred;
if (hasReadonly(pred)) {
assertType<Equal<typeof pred, P & AsReadonly>>(true);
}
});
});
11 changes: 11 additions & 0 deletions assert_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,17 @@ Deno.test("assert", async (t) => {
);
});

await t.step(
"throws an `AssertError` on false predicate with an anonymous predicate",
() => {
assertThrows(
() => assert(x, (_x: unknown): _x is string => false),
AssertError,
`Expected a value that satisfies the predicate anonymous predicate, got symbol: undefined`,
);
},
);

await t.step(
"throws an `AssertError` on false predicate with a custom name",
() => {
Expand Down
33 changes: 30 additions & 3 deletions is/parameters_of_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,13 @@ Deno.test("isParametersOf<T>", async (t) => {
const predTup = [is.Number, is.String, as.Optional(is.Boolean)] as const;
assertEquals(isParametersOf(predTup)([0, "a", true]), true);
assertEquals(isParametersOf(predTup)([0, "a"]), true);
assertEquals(isParametersOf(predTup)([0, "a", undefined]), true);
});

await t.step("returns false on non T tuple", () => {
const predTup = [is.Number, is.String, as.Optional(is.Boolean)] as const;
assertEquals(isParametersOf(predTup)([0, 1, 2]), false);
assertEquals(isParametersOf(predTup)([0]), false);
assertEquals(isParametersOf(predTup)([0, "a", true, 0]), false);
});

Expand Down Expand Up @@ -107,17 +109,42 @@ Deno.test("isParametersOf<T, R>", async (t) => {
await t.step("returns false on non T tuple", () => {
const predTup = [is.Number, is.String, as.Optional(is.Boolean)] as const;
const predRest = is.ArrayOf(is.String);
assertEquals(isParametersOf(predTup, predRest)([0, 1, 2, 0, 1, 2]), false);
assertEquals(isParametersOf(predTup, predRest)([0, "a", 0, 1, 2]), false);
assertEquals(isParametersOf(predTup, predRest)("a"), false, "Not an array");
assertEquals(
isParametersOf(predTup, predRest)([0]),
false,
"Less than `predTup.length` - optional-count",
);
assertEquals(
isParametersOf(predTup, predRest)([0, 1, 2]),
false,
"Not match `predTup` and no rest elements",
);
assertEquals(
isParametersOf(predTup, predRest)([0, 1, 2, 0, 1, 2]),
false,
"Not match `predTup` and `predRest`",
);
assertEquals(
isParametersOf(predTup, predRest)([0, "a", true, 0, 1, 2]),
false,
"Match `predTup` but not match `predRest`",
);
assertEquals(
isParametersOf(predTup, predRest)([0, "a", undefined, 0, 1, 2]),
false,
"Match `predTup` but not match `predRest`",
);
assertEquals(
isParametersOf(predTup, predRest)([0, "a", "b", "a", "b", "c"]),
false,
"Match `predRest` but not match `predTup`",
);
assertEquals(
isParametersOf(predTup, predRest)([0, "a", "a", "b", "c"]),
false,
"Match `predRest` but no optional parameters",
);
assertEquals(isParametersOf(predTup, predRest)([0, "a", "b"]), false);
});

await t.step("predicated type is correct", () => {
Expand Down
11 changes: 11 additions & 0 deletions is/record_object_of_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Deno.test("isRecordObjectOf<T>", async (t) => {
});

await t.step("returns false on non T record", () => {
assertEquals(isRecordObjectOf(is.String)("a"), false, "Not a Record");
assertEquals(isRecordObjectOf(is.String)({ a: 0 }), false);
assertEquals(isRecordObjectOf(is.Number)({ a: "a" }), false);
assertEquals(isRecordObjectOf(is.String)({ a: true }), false);
Expand Down Expand Up @@ -91,6 +92,11 @@ Deno.test("isRecordObjectOf<T, K>", async (t) => {
});

await t.step("returns false on non K record", () => {
assertEquals(
isRecordObjectOf(is.String, is.String)("a"),
false,
"Not a Record",
);
assertEquals(isRecordObjectOf(is.Number, is.Number)({ a: 0 }), false);
assertEquals(isRecordObjectOf(is.String, is.Number)({ a: "a" }), false);
assertEquals(isRecordObjectOf(is.Boolean, is.Number)({ a: true }), false);
Expand Down Expand Up @@ -161,6 +167,11 @@ Deno.test("isRecordObjectOf<T, K>", async (t) => {
});

await t.step("returns false on non T record", () => {
assertEquals(
isRecordObjectOf(is.String, is.Symbol)("a"),
false,
"Not a Record",
);
assertEquals(isRecordObjectOf(is.String, is.Symbol)({ [a]: 0 }), false);
assertEquals(isRecordObjectOf(is.Number, is.Symbol)({ [a]: "a" }), false);
assertEquals(
Expand Down
7 changes: 7 additions & 0 deletions is/record_of_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Deno.test("isRecordOf<T>", async (t) => {
});

await t.step("returns false on non T record", () => {
assertEquals(isRecordOf(is.String)("a"), false, "Not a Record");
assertEquals(isRecordOf(is.String)({ a: 0 }), false);
assertEquals(isRecordOf(is.Number)({ a: "a" }), false);
assertEquals(isRecordOf(is.String)({ a: true }), false);
Expand Down Expand Up @@ -85,6 +86,7 @@ Deno.test("isRecordOf<T, K>", async (t) => {
});

await t.step("returns false on non T record", () => {
assertEquals(isRecordOf(is.String, is.String)("a"), false, "Not a Record");
assertEquals(isRecordOf(is.String, is.String)({ a: 0 }), false);
assertEquals(isRecordOf(is.Number, is.String)({ a: "a" }), false);
assertEquals(isRecordOf(is.String, is.String)({ a: true }), false);
Expand Down Expand Up @@ -158,6 +160,11 @@ Deno.test("isRecordOf<T, K>", async (t) => {
});

await t.step("returns false on non T record", () => {
assertEquals(
isRecordOf(is.String, is.Symbol)("a"),
false,
"Not a Record",
);
assertEquals(isRecordOf(is.String, is.Symbol)({ [a]: 0 }), false);
assertEquals(isRecordOf(is.Number, is.Symbol)({ [a]: "a" }), false);
assertEquals(isRecordOf(is.String, is.Symbol)({ [a]: true }), false);
Expand Down
1 change: 1 addition & 0 deletions is/set_of_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Deno.test("isSetOf<T>", async (t) => {
});

await t.step("returns false on non T set", () => {
assertEquals(isSetOf(is.String)("a"), false, "Not a Set");
assertEquals(isSetOf(is.String)(new Set([0, 1, 2])), false);
assertEquals(isSetOf(is.Number)(new Set(["a", "b", "c"])), false);
assertEquals(isSetOf(is.String)(new Set([true, false, true])), false);
Expand Down