├── .github ├── FUNDING.yml └── workflows │ ├── bench.yml │ ├── jsr.yml │ └── test.yml ├── .gitignore ├── .gitmessage ├── .scripts └── gen-mod.ts ├── LICENSE ├── README.md ├── __snapshots__ └── _inspect_test.ts.snap ├── _annotation.ts ├── _funcutil.ts ├── _inspect.ts ├── _inspect_test.ts ├── _testutil.ts ├── _typeutil.ts ├── as ├── __snapshots__ │ └── readonly_test.ts.snap ├── mod.ts ├── mod_test.ts ├── optional.ts ├── optional_bench.ts ├── optional_test.ts ├── readonly.ts ├── readonly_bench.ts └── readonly_test.ts ├── assert.ts ├── assert_test.ts ├── deno.jsonc ├── ensure.ts ├── ensure_test.ts ├── is ├── __snapshots__ │ ├── intersection_of_test.ts.snap │ ├── object_of_test.ts.snap │ ├── omit_of_test.ts.snap │ ├── parameters_of_test.ts.snap │ ├── partial_of_test.ts.snap │ ├── pick_of_test.ts.snap │ ├── readonly_of_test.ts.snap │ ├── record_object_of_test.ts.snap │ ├── record_of_test.ts.snap │ ├── required_of_test.ts.snap │ ├── set_of_test.ts.snap │ ├── strict_of_test.ts.snap │ ├── tuple_of_test.ts.snap │ ├── uniform_tuple_of_test.ts.snap │ └── union_of_test.ts.snap ├── any.ts ├── any_bench.ts ├── any_test.ts ├── array.ts ├── array_bench.ts ├── array_of.ts ├── array_of_bench.ts ├── array_of_test.ts ├── array_test.ts ├── async_function.ts ├── async_function_bench.ts ├── async_function_test.ts ├── bigint.ts ├── bigint_bench.ts ├── bigint_test.ts ├── boolean.ts ├── boolean_bench.ts ├── boolean_test.ts ├── custom_jsonable.ts ├── custom_jsonable_bench.ts ├── custom_jsonable_test.ts ├── function.ts ├── function_bench.ts ├── function_test.ts ├── instance_of.ts ├── instance_of_bench.ts ├── instance_of_test.ts ├── intersection_of.ts ├── intersection_of_bench.ts ├── intersection_of_test.ts ├── jsonable.ts ├── jsonable_bench.ts ├── jsonable_test.ts ├── literal_of.ts ├── literal_of_bench.ts ├── literal_of_test.ts ├── literal_one_of.ts ├── literal_one_of_bench.ts ├── literal_one_of_test.ts ├── map.ts ├── map_bench.ts ├── map_of.ts ├── map_of_bench.ts ├── map_of_test.ts ├── map_test.ts ├── mod.ts ├── mod_test.ts ├── null.ts ├── null_bench.ts ├── null_test.ts ├── nullish.ts ├── nullish_bench.ts ├── nullish_test.ts ├── number.ts ├── number_bench.ts ├── number_test.ts ├── object_of.ts ├── object_of_bench.ts ├── object_of_test.ts ├── omit_of.ts ├── omit_of_bench.ts ├── omit_of_test.ts ├── parameters_of.ts ├── parameters_of_bench.ts ├── parameters_of_test.ts ├── partial_of.ts ├── partial_of_bench.ts ├── partial_of_test.ts ├── pick_of.ts ├── pick_of_bench.ts ├── pick_of_test.ts ├── primitive.ts ├── primitive_bench.ts ├── primitive_test.ts ├── readonly_of.ts ├── readonly_of_bench.ts ├── readonly_of_test.ts ├── record.ts ├── record_bench.ts ├── record_object.ts ├── record_object_bench.ts ├── record_object_of.ts ├── record_object_of_bench.ts ├── record_object_of_test.ts ├── record_object_test.ts ├── record_of.ts ├── record_of_bench.ts ├── record_of_test.ts ├── record_test.ts ├── required_of.ts ├── required_of_bench.ts ├── required_of_test.ts ├── set.ts ├── set_bench.ts ├── set_of.ts ├── set_of_bench.ts ├── set_of_test.ts ├── set_test.ts ├── strict_of.ts ├── strict_of_bench.ts ├── strict_of_test.ts ├── string.ts ├── string_bench.ts ├── string_test.ts ├── symbol.ts ├── symbol_bench.ts ├── symbol_test.ts ├── sync_function.ts ├── sync_function_bench.ts ├── sync_function_test.ts ├── tuple_of.ts ├── tuple_of_bench.ts ├── tuple_of_test.ts ├── undefined.ts ├── undefined_bench.ts ├── undefined_test.ts ├── uniform_tuple_of.ts ├── uniform_tuple_of_bench.ts ├── uniform_tuple_of_test.ts ├── union_of.ts ├── union_of_bench.ts ├── union_of_test.ts ├── unknown.ts ├── unknown_bench.ts └── unknown_test.ts ├── maybe.ts ├── maybe_test.ts ├── mod.ts ├── mod_test.ts └── type.ts /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [ 4 | lambdalisue, 5 | ] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 6 | patreon: # Replace with a single Patreon username 7 | open_collective: # Replace with a single Open Collective username 8 | ko_fi: # Replace with a single Ko-fi username 9 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 10 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 11 | liberapay: # Replace with a single Liberapay username 12 | issuehunt: # Replace with a single IssueHunt username 13 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 14 | polar: # Replace with a single Polar username 15 | buy_me_a_coffee: # Replace with a single Buy Me a Coffee username 16 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 17 | -------------------------------------------------------------------------------- /.github/workflows/bench.yml: -------------------------------------------------------------------------------- 1 | name: Benchmark 2 | 3 | env: 4 | DENO_VERSION: 1.x 5 | 6 | on: 7 | push: 8 | tags: 9 | - "v*" 10 | workflow_dispatch: 11 | 12 | jobs: 13 | bench: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v4 17 | - uses: denoland/setup-deno@v1 18 | with: 19 | deno-version: ${{ env.DENO_VERSION }} 20 | - name: Cache 21 | run: | 22 | deno cache **/*.ts 23 | - name: Test 24 | run: | 25 | deno bench | tee bench.txt 26 | echo '# 🚀 Benchmarks' >> $GITHUB_STEP_SUMMARY 27 | echo '```' >> $GITHUB_STEP_SUMMARY 28 | cat bench.txt | npx strip-ansi-cli >> $GITHUB_STEP_SUMMARY 29 | echo '```' >> $GITHUB_STEP_SUMMARY 30 | timeout-minutes: 10 31 | -------------------------------------------------------------------------------- /.github/workflows/jsr.yml: -------------------------------------------------------------------------------- 1 | name: jsr 2 | 3 | env: 4 | DENO_VERSION: 1.x 5 | 6 | on: 7 | push: 8 | tags: 9 | - "v*" 10 | 11 | permissions: 12 | contents: read 13 | id-token: write 14 | 15 | jobs: 16 | publish: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v4 20 | with: 21 | fetch-depth: 0 22 | - uses: denoland/setup-deno@v1 23 | with: 24 | deno-version: ${{ env.DENO_VERSION }} 25 | - name: Publish 26 | run: | 27 | deno run -A jsr:@david/publish-on-tag@0.1.3 28 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | env: 4 | DENO_VERSION: 1.x 5 | 6 | on: 7 | push: 8 | branches: 9 | - main 10 | pull_request: 11 | workflow_dispatch: 12 | 13 | jobs: 14 | check: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v4 18 | - uses: denoland/setup-deno@v1 19 | with: 20 | deno-version: ${{ env.DENO_VERSION }} 21 | - name: Format 22 | run: | 23 | deno fmt --check 24 | - name: Lint 25 | run: deno lint 26 | - name: Type check 27 | run: deno task check 28 | - name: Gen check 29 | run: | 30 | deno task gen 31 | git diff --exit-code 32 | 33 | test: 34 | runs-on: ubuntu-latest 35 | steps: 36 | - uses: actions/checkout@v4 37 | - uses: denoland/setup-deno@v1 38 | with: 39 | deno-version: ${{ env.DENO_VERSION }} 40 | - name: Cache 41 | run: | 42 | deno cache **/*.ts 43 | timeout-minutes: 5 44 | - name: Test 45 | run: | 46 | deno task test:coverage 47 | timeout-minutes: 5 48 | - run: | 49 | deno task coverage --lcov > coverage.lcov 50 | - uses: codecov/codecov-action@v4 51 | with: 52 | os: ${{ runner.os }} 53 | files: ./coverage.lcov 54 | token: ${{ secrets.CODECOV_TOKEN }} 55 | 56 | jsr-publish: 57 | runs-on: ubuntu-latest 58 | steps: 59 | - uses: actions/checkout@v4 60 | - uses: denoland/setup-deno@v1 61 | with: 62 | deno-version: ${{ env.DENO_VERSION }} 63 | - name: Publish (dry-run) 64 | run: | 65 | deno publish --dry-run 66 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /docs 2 | deno.lock 3 | .coverage 4 | -------------------------------------------------------------------------------- /.gitmessage: -------------------------------------------------------------------------------- 1 | 2 | # **Conventional Commits** 3 | # 4 | # [optional scope]: 5 | # 6 | # feat: feature (minor) 7 | # deps: dependencies (minor/patch) 8 | # fix: bug fix (patch) 9 | # refactor: refactoring code 10 | # test: test fix; no code change 11 | # docs: documentation fix; no code change 12 | # style: formatting, missing semi colons, etc; no code change 13 | # chore: updating build tasks, package manager configs, etc; no code change 14 | # 15 | # **Install** 16 | # 17 | # git config commit.template .gitmessage 18 | # 19 | # **Reference** 20 | # 21 | # - https://www.conventionalcommits.org/en/v1.0.0/ 22 | -------------------------------------------------------------------------------- /.scripts/gen-mod.ts: -------------------------------------------------------------------------------- 1 | import { 2 | fromFileUrl, 3 | globToRegExp, 4 | join, 5 | relative, 6 | toFileUrl, 7 | } from "@std/path"; 8 | import { map } from "@core/iterutil/map"; 9 | import { flatMap } from "@core/iterutil/async/flat-map"; 10 | import { doc } from "@deno/doc"; 11 | 12 | const excludes = [ 13 | "mod.ts", 14 | "*_test.ts", 15 | "*_bench.ts", 16 | "_*.ts", 17 | ]; 18 | 19 | async function* iterModules(path: string): AsyncIterable { 20 | const patterns = excludes.map((p) => globToRegExp(p)); 21 | for await (const entry of Deno.readDir(path)) { 22 | if (!entry.isFile || !entry.name.endsWith(".ts")) continue; 23 | if (patterns.some((p) => p.test(entry.name))) continue; 24 | yield join(path, entry.name); 25 | } 26 | } 27 | 28 | async function generateModTs( 29 | namespace: string, 30 | ): Promise { 31 | const path = fromFileUrl(import.meta.resolve(`../${namespace}/`)); 32 | const exports = (await Array.fromAsync( 33 | flatMap(iterModules(path), (x) => doc(toFileUrl(x).href)), 34 | )) 35 | .filter((x) => !!x.jsDoc?.doc) 36 | .filter((x) => x.kind === "function") 37 | .filter((x) => x.declarationKind === "export") 38 | .filter((x) => x.name.startsWith(namespace)) 39 | .map((x) => ({ 40 | path: relative(path, fromFileUrl(x.location.filename)), 41 | name: x.name, 42 | doc: x.jsDoc!.doc!, 43 | })) 44 | .toSorted((a, b) => a.name.localeCompare(b.name)); 45 | const lines = [ 46 | "// NOTE: This file is generated by gen-mod.ts", 47 | ...exports.map((x) => { 48 | return `import { ${x.name} } from "./${x.path}";`; 49 | }), 50 | "", 51 | ...map((new Set(exports.map((x) => x.path))).values(), (x) => { 52 | return `export * from "./${x}";`; 53 | }), 54 | "", 55 | "/**", 56 | ` * An object containing all the functions in ${namespace} module.`, 57 | " */", 58 | `export const ${namespace}: {`, 59 | ...exports.flatMap((x) => { 60 | return [ 61 | " /**", 62 | ...x.doc.split("\n").map((line) => ` * ${line}`.trimEnd()), 63 | " */", 64 | ` ${x.name.replace(namespace, "")}: typeof ${x.name};`.trimEnd(), 65 | ]; 66 | }), 67 | "} = {", 68 | ...exports.flatMap((x) => { 69 | return [ 70 | ` ${x.name.replace(namespace, "")}: ${x.name},`.trimEnd(), 71 | ]; 72 | }), 73 | "};", 74 | ]; 75 | await Deno.writeTextFile(join(path, "mod.ts"), lines.join("\n") + "\n"); 76 | } 77 | 78 | async function main(): Promise { 79 | await generateModTs("is"); 80 | await generateModTs("as"); 81 | } 82 | 83 | if (import.meta.main) { 84 | main().catch((err) => { 85 | console.error(err); 86 | Deno.exit(1); 87 | }); 88 | } 89 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2024 jsr-core 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | 21 | -------------------------------------------------------------------------------- /__snapshots__/_inspect_test.ts.snap: -------------------------------------------------------------------------------- 1 | export const snapshot = {}; 2 | 3 | snapshot[`inspect > string 1`] = `'"hello world"'`; 4 | 5 | snapshot[`inspect > number 1`] = `"100"`; 6 | 7 | snapshot[`inspect > bigint 1`] = `"100n"`; 8 | 9 | snapshot[`inspect > boolean 1`] = `"true"`; 10 | 11 | snapshot[`inspect > array 1`] = `"[]"`; 12 | 13 | snapshot[`inspect > array 2`] = `"[0, 1, 2]"`; 14 | 15 | snapshot[`inspect > array 3`] = `'[0, "a", true]'`; 16 | 17 | snapshot[`inspect > array 4`] = `"[0, [1, [2]]]"`; 18 | 19 | snapshot[`inspect > record 1`] = `"{}"`; 20 | 21 | snapshot[`inspect > record 2`] = `"{a: 0, b: 1, c: 2}"`; 22 | 23 | snapshot[`inspect > record 3`] = ` 24 | '{ 25 | a: "a", 26 | b: 1, 27 | c: true 28 | }' 29 | `; 30 | 31 | snapshot[`inspect > record 4`] = `"{a: {b: {c: 0}}}"`; 32 | 33 | snapshot[`inspect > record 5`] = `"{Symbol(a): 0}"`; 34 | 35 | snapshot[`inspect > record 6`] = ` 36 | "{ 37 | a: 0, 38 | c: true, 39 | Symbol(b): 1 40 | }" 41 | `; 42 | 43 | snapshot[`inspect > record 7`] = ` 44 | "{ 45 | Symbol(a): { 46 | Symbol(b): {Symbol(c): 0} 47 | } 48 | }" 49 | `; 50 | 51 | snapshot[`inspect > function 1`] = `"inspect"`; 52 | 53 | snapshot[`inspect > function 2`] = `"(anonymous)"`; 54 | 55 | snapshot[`inspect > null 1`] = `"null"`; 56 | 57 | snapshot[`inspect > undefined 1`] = `"undefined"`; 58 | 59 | snapshot[`inspect > symbol 1`] = `"Symbol(a)"`; 60 | 61 | snapshot[`inspect > date 1`] = `"Date"`; 62 | 63 | snapshot[`inspect > promise 1`] = `"Promise"`; 64 | -------------------------------------------------------------------------------- /_annotation.ts: -------------------------------------------------------------------------------- 1 | import type { Predicate } from "./type.ts"; 2 | 3 | // deno-lint-ignore no-explicit-any 4 | export type Fn = (...args: any[]) => unknown; 5 | 6 | export function annotate( 7 | fn: F, 8 | name: N, 9 | value: V, 10 | ): F & { [K in N]: V } { 11 | return Object.defineProperties(fn, { 12 | [name]: { 13 | value, 14 | }, 15 | }) as F & { [K in N]: V }; 16 | } 17 | 18 | export function unannotate( 19 | fn: F & { [K in N]: V }, 20 | name: N, 21 | ): V { 22 | return fn[name]; 23 | } 24 | 25 | export function hasAnnotation( 26 | fn: F, 27 | name: N, 28 | ): fn is F & { [K in N]: unknown } { 29 | // deno-lint-ignore no-explicit-any 30 | return !!(fn as any)[name]; 31 | } 32 | 33 | /** 34 | * Annotation for optional. 35 | */ 36 | export type AsOptional = { 37 | optional: Predicate; 38 | }; 39 | 40 | /** 41 | * Annotation for readonly. 42 | */ 43 | export type AsReadonly = { 44 | readonly: Predicate; 45 | }; 46 | 47 | /** 48 | * Annotation for predObj. 49 | */ 50 | export type IsPredObj< 51 | T extends Record> = Record< 52 | PropertyKey, 53 | Predicate 54 | >, 55 | > = { 56 | predObj: T; 57 | }; 58 | -------------------------------------------------------------------------------- /_funcutil.ts: -------------------------------------------------------------------------------- 1 | import { inspect } from "./_inspect.ts"; 2 | 3 | /** 4 | * Rewrite the function name. 5 | */ 6 | // deno-lint-ignore no-explicit-any 7 | export function rewriteName unknown>( 8 | fn: F, 9 | name: string, 10 | ...args: unknown[] 11 | ): F { 12 | let cachedName: string | undefined; 13 | return Object.defineProperties(fn, { 14 | name: { 15 | get: () => { 16 | if (cachedName) return cachedName; 17 | cachedName = `${name}(${args.map((v) => inspect(v)).join(", ")})`; 18 | return cachedName; 19 | }, 20 | }, 21 | }); 22 | } 23 | -------------------------------------------------------------------------------- /_inspect.ts: -------------------------------------------------------------------------------- 1 | const defaultThreshold = 20; 2 | 3 | export type InspectOptions = { 4 | // The maximum number of characters of a single attribute 5 | threshold?: number; 6 | }; 7 | 8 | /** 9 | * Inspect a value 10 | */ 11 | export function inspect(value: unknown, options: InspectOptions = {}): string { 12 | if (value === null) { 13 | return "null"; 14 | } else if (Array.isArray(value)) { 15 | return inspectArray(value, options); 16 | } 17 | switch (typeof value) { 18 | case "string": 19 | return JSON.stringify(value); 20 | case "bigint": 21 | return `${value}n`; 22 | case "object": 23 | if (value.constructor?.name !== "Object") { 24 | return value.constructor?.name; 25 | } 26 | return inspectRecord(value as Record, options); 27 | case "function": 28 | return value.name || "(anonymous)"; 29 | } 30 | return value?.toString() ?? "undefined"; 31 | } 32 | 33 | function inspectArray(value: unknown[], options: InspectOptions): string { 34 | const { threshold = defaultThreshold } = options; 35 | const vs = value.map((v) => inspect(v, options)); 36 | const s = vs.join(", "); 37 | if (s.length <= threshold) return `[${s}]`; 38 | const m = vs.join(",\n"); 39 | return `[\n${indent(2, m)}\n]`; 40 | } 41 | 42 | function inspectRecord( 43 | value: Record, 44 | options: InspectOptions, 45 | ): string { 46 | const { threshold = defaultThreshold } = options; 47 | const vs = [...Object.keys(value), ...Object.getOwnPropertySymbols(value)] 48 | .map((k) => `${k.toString()}: ${inspect(value[k], options)}`); 49 | const s = vs.join(", "); 50 | if (s.length <= threshold) return `{${s}}`; 51 | const m = vs.join(",\n"); 52 | return `{\n${indent(2, m)}\n}`; 53 | } 54 | 55 | function indent(level: number, text: string): string { 56 | const prefix = " ".repeat(level); 57 | return text.split("\n").map((line) => `${prefix}${line}`).join("\n"); 58 | } 59 | -------------------------------------------------------------------------------- /_inspect_test.ts: -------------------------------------------------------------------------------- 1 | import { assertSnapshot } from "@std/testing/snapshot"; 2 | import { inspect } from "./_inspect.ts"; 3 | 4 | Deno.test("inspect", async (t) => { 5 | await t.step("string", async (t) => { 6 | await assertSnapshot(t, inspect("hello world")); 7 | }); 8 | await t.step("number", async (t) => { 9 | await assertSnapshot(t, inspect(100)); 10 | }); 11 | await t.step("bigint", async (t) => { 12 | await assertSnapshot(t, inspect(100n)); 13 | }); 14 | await t.step("boolean", async (t) => { 15 | await assertSnapshot(t, inspect(true)); 16 | }); 17 | await t.step("array", async (t) => { 18 | await assertSnapshot(t, inspect([])); 19 | await assertSnapshot(t, inspect([0, 1, 2])); 20 | await assertSnapshot(t, inspect([0, "a", true])); 21 | await assertSnapshot(t, inspect([0, [1, [2]]])); 22 | }); 23 | await t.step("record", async (t) => { 24 | await assertSnapshot(t, inspect({})); 25 | await assertSnapshot(t, inspect({ a: 0, b: 1, c: 2 })); 26 | await assertSnapshot(t, inspect({ a: "a", b: 1, c: true })); 27 | await assertSnapshot(t, inspect({ a: { b: { c: 0 } } })); 28 | await assertSnapshot(t, inspect({ [Symbol("a")]: 0 })); 29 | await assertSnapshot(t, inspect({ a: 0, [Symbol("b")]: 1, c: true })); 30 | await assertSnapshot( 31 | t, 32 | inspect({ [Symbol("a")]: { [Symbol("b")]: { [Symbol("c")]: 0 } } }), 33 | ); 34 | }); 35 | await t.step("function", async (t) => { 36 | await assertSnapshot(t, inspect(inspect)); 37 | await assertSnapshot(t, inspect(() => {})); 38 | }); 39 | await t.step("null", async (t) => { 40 | await assertSnapshot(t, inspect(null)); 41 | }); 42 | await t.step("undefined", async (t) => { 43 | await assertSnapshot(t, inspect(undefined)); 44 | }); 45 | await t.step("symbol", async (t) => { 46 | await assertSnapshot(t, inspect(Symbol("a"))); 47 | }); 48 | await t.step("date", async (t) => { 49 | await assertSnapshot(t, inspect(new Date())); 50 | }); 51 | await t.step("promise", async (t) => { 52 | await assertSnapshot(t, inspect(new Promise(() => {}))); 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /_testutil.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from "@std/assert"; 2 | import type { Predicate } from "./type.ts"; 3 | 4 | const examples = { 5 | string: ["", "Hello world"], 6 | number: [0, 1234567890], 7 | bigint: [0n, 1234567890n], 8 | boolean: [true, false], 9 | array: [[], [0, 1, 2], ["a", "b", "c"], [0, "a", true]], 10 | set: [new Set(), new Set([0, 1, 2]), new Set(["a", "b", "c"])], 11 | record: [{}, { a: 0, b: 1, c: 2 }, { a: "a", b: "b", c: "c" }], 12 | map: [ 13 | new Map(), 14 | new Map([["a", 0], ["b", 1], ["c", 2]]), 15 | new Map([["a", "a"], ["b", "b"], ["c", "c"]]), 16 | ], 17 | syncFunction: [function a() {}, () => {}], 18 | asyncFunction: [async function b() {}, async () => {}], 19 | null: [null], 20 | undefined: [undefined], 21 | symbol: [Symbol("a"), Symbol("b"), Symbol("c")], 22 | date: [new Date(1690248225000), new Date(0)], 23 | promise: [new Promise(() => {})], 24 | } as const; 25 | 26 | export async function testWithExamples( 27 | t: Deno.TestContext, 28 | pred: Predicate, 29 | opts?: { 30 | validExamples?: (keyof typeof examples)[]; 31 | excludeExamples?: (keyof typeof examples)[]; 32 | }, 33 | ): Promise { 34 | const { validExamples = [], excludeExamples = [] } = opts ?? {}; 35 | const exampleEntries = (Object.entries(examples) as unknown as [ 36 | name: keyof typeof examples, 37 | example: unknown[], 38 | ][]).filter(([k]) => !excludeExamples.includes(k)); 39 | for (const [name, example] of exampleEntries) { 40 | const expect = validExamples.includes(name); 41 | for (const v of example) { 42 | await t.step( 43 | `returns ${expect} on ${stringify(v)}`, 44 | () => { 45 | assertEquals(pred(v), expect); 46 | }, 47 | ); 48 | } 49 | } 50 | } 51 | 52 | export function stringify(x: unknown): string { 53 | if (x instanceof Date) return `Date(${x.valueOf()})`; 54 | if (x instanceof Promise) return "Promise"; 55 | if (x instanceof Set) return `Set(${stringify([...x.values()])})`; 56 | if (x instanceof Map) return `Map(${stringify([...x.entries()])})`; 57 | if (typeof x === "function") return x.toString(); 58 | if (typeof x === "bigint") return `${x}n`; 59 | if (typeof x === "symbol") return x.toString(); 60 | return JSON.stringify(x); 61 | } 62 | -------------------------------------------------------------------------------- /_typeutil.ts: -------------------------------------------------------------------------------- 1 | export type FlatType = T extends Record 2 | ? { [K in keyof T]: FlatType } 3 | : T; 4 | -------------------------------------------------------------------------------- /as/__snapshots__/readonly_test.ts.snap: -------------------------------------------------------------------------------- 1 | export const snapshot = {}; 2 | 3 | snapshot[`asReadonly > returns properly named function 1`] = `"asReadonly(isNumber)"`; 4 | 5 | snapshot[`asReadonly > returns properly named function 2`] = `"asReadonly(isNumber)"`; 6 | 7 | snapshot[`asUnreadonly > returns properly named function 1`] = `"isNumber"`; 8 | 9 | snapshot[`asUnreadonly > returns properly named function 2`] = `"isNumber"`; 10 | 11 | snapshot[`asUnreadonly > returns properly named function 3`] = `"isNumber"`; 12 | -------------------------------------------------------------------------------- /as/mod_test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from "@std/assert"; 2 | import { globToRegExp } from "@std/path"; 3 | import { as } from "./mod.ts"; 4 | 5 | const excludes = [ 6 | "mod.ts", 7 | "_*.ts", 8 | "*_bench.ts", 9 | "*_test.ts", 10 | ]; 11 | 12 | Deno.test("as", async (t) => { 13 | // List all files under the directory 14 | const names = await listAsFunctions(); 15 | await t.step( 16 | "must have all `as*` function aliases as entries", 17 | () => { 18 | assertEquals(Object.keys(as).sort(), names); 19 | }, 20 | ); 21 | }); 22 | 23 | async function listAsFunctions(): Promise { 24 | const patterns = excludes.map((p) => globToRegExp(p)); 25 | const names: string[] = []; 26 | for await (const entry of Deno.readDir(import.meta.dirname!)) { 27 | if (!entry.isFile || !entry.name.endsWith(".ts")) continue; 28 | if (patterns.some((p) => p.test(entry.name))) continue; 29 | const mod = await import(import.meta.resolve(`./${entry.name}`)); 30 | const isFunctionNames = Object.entries(mod) 31 | .filter(([k, _]) => k.startsWith("as")) 32 | .map(([k, _]) => k.slice(2)); 33 | names.push(...isFunctionNames); 34 | } 35 | return names.toSorted(); 36 | } 37 | -------------------------------------------------------------------------------- /as/optional.ts: -------------------------------------------------------------------------------- 1 | import { rewriteName } from "../_funcutil.ts"; 2 | import type { Predicate, PredicateType } from "../type.ts"; 3 | import { 4 | annotate, 5 | type AsOptional, 6 | hasAnnotation, 7 | unannotate, 8 | } from "../_annotation.ts"; 9 | 10 | /** 11 | * Annotate the given predicate function as optional. 12 | * 13 | * Use this function to annotate a predicate function of `predObj` in {@linkcode [is/object-of].isObjectOf|isObjectOf}. 14 | * 15 | * Note that the annotated predicate function will return `true` if the type of `x` is `T` or `undefined`, indicating that 16 | * this function is not just for annotation but it also changes the behavior of the predicate function. 17 | * 18 | * Use {@linkcode asUnoptional} to remove the annotation. 19 | * Use {@linkcode hasOptional} to check if a predicate function has annotated with this function. 20 | * 21 | * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. 22 | * 23 | * ```ts 24 | * import { as, is } from "@core/unknownutil"; 25 | * 26 | * const isMyType = is.ObjectOf({ 27 | * foo: as.Optional(is.String), 28 | * }); 29 | * const a: unknown = {}; 30 | * if (isMyType(a)) { 31 | * const _: {foo?: string | undefined} = a; 32 | * } 33 | * ``` 34 | */ 35 | export function asOptional

>( 36 | pred: P, 37 | ): 38 | & Extract>> 39 | & Predicate | undefined> 40 | & AsOptional> { 41 | if (hasAnnotation(pred, "optional")) { 42 | return pred as 43 | & Extract>> 44 | & Predicate | undefined> 45 | & AsOptional>; 46 | } 47 | return rewriteName( 48 | annotate( 49 | (x) => x === undefined || pred(x), 50 | "optional", 51 | pred, 52 | ), 53 | "asOptional", 54 | pred, 55 | ) as unknown as 56 | & Extract>> 57 | & Predicate | undefined> 58 | & AsOptional>; 59 | } 60 | 61 | /** 62 | * Unannotate the annotated predicate function with {@linkcode asOptional}. 63 | * 64 | * Use this function to unannotate a predicate function of `predObj` in {@linkcode [is/object-of].isObjectOf|isObjectOf}. 65 | * 66 | * Note that the annotated predicate function will return `true` if the type of `x` is `T`, indicating that 67 | * this function is not just for annotation but it also changes the behavior of the predicate function. 68 | * 69 | * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. 70 | * 71 | * ```ts 72 | * import { as, is } from "@core/unknownutil"; 73 | * 74 | * const isMyType = is.ObjectOf({ 75 | * foo: as.Unoptional(as.Optional(is.String)), 76 | * }); 77 | * const a: unknown = {foo: "a"}; 78 | * if (isMyType(a)) { 79 | * const _: {foo: string} = a; 80 | * } 81 | * ``` 82 | */ 83 | export function asUnoptional< 84 | P extends Predicate, 85 | T extends P extends Predicate ? T 86 | : P extends Predicate ? T 87 | : never, 88 | >(pred: P): Predicate { 89 | if (!hasAnnotation(pred, "optional")) { 90 | return pred as Predicate; 91 | } 92 | return unannotate(pred, "optional") as Predicate; 93 | } 94 | 95 | /** 96 | * Check if the given type predicate has optional annotation. 97 | */ 98 | export function hasOptional< 99 | P extends Predicate, 100 | T extends P extends Predicate ? T 101 | : P extends Predicate ? T 102 | : never, 103 | >( 104 | pred: P, 105 | ): pred is P & AsOptional { 106 | return hasAnnotation(pred, "optional"); 107 | } 108 | -------------------------------------------------------------------------------- /as/optional_bench.ts: -------------------------------------------------------------------------------- 1 | import { 2 | asOptional as asOptional420, 3 | asUnoptional as asUnoptional420, 4 | hasOptional as hasOptional420, 5 | } from "jsr:@core/unknownutil@4.2.0/as/optional"; 6 | import { asOptional, asUnoptional, hasOptional } from "./optional.ts"; 7 | 8 | const repeats = Array.from({ length: 100 }); 9 | 10 | const predicate = (x: unknown): x is string => typeof x === "string"; 11 | const optional = asOptional((x: unknown): x is string => typeof x === "string"); 12 | 13 | Deno.bench({ 14 | name: "current", 15 | fn() { 16 | repeats.forEach(() => asOptional(predicate)); 17 | }, 18 | group: "asOptional (predicate)", 19 | }); 20 | 21 | Deno.bench({ 22 | name: "current", 23 | fn() { 24 | repeats.forEach(() => asOptional(optional)); 25 | }, 26 | group: "asOptional (optional)", 27 | }); 28 | 29 | Deno.bench({ 30 | name: "current", 31 | fn() { 32 | repeats.forEach(() => asUnoptional(predicate)); 33 | }, 34 | group: "asUnoptional (predicate)", 35 | }); 36 | 37 | Deno.bench({ 38 | name: "current", 39 | fn() { 40 | repeats.forEach(() => asUnoptional(optional)); 41 | }, 42 | group: "asUnoptional (optional)", 43 | }); 44 | 45 | Deno.bench({ 46 | name: "current", 47 | fn() { 48 | repeats.forEach(() => hasOptional(predicate)); 49 | }, 50 | group: "hasOptional (predicate)", 51 | }); 52 | 53 | Deno.bench({ 54 | name: "current", 55 | fn() { 56 | repeats.forEach(() => hasOptional(optional)); 57 | }, 58 | group: "hasOptional (optional)", 59 | }); 60 | 61 | Deno.bench({ 62 | name: "v4.2.0", 63 | fn() { 64 | repeats.forEach(() => asOptional420(predicate)); 65 | }, 66 | group: "asOptional (predicate)", 67 | }); 68 | 69 | Deno.bench({ 70 | name: "v4.2.0", 71 | fn() { 72 | repeats.forEach(() => asOptional420(optional)); 73 | }, 74 | group: "asOptional (optional)", 75 | }); 76 | 77 | Deno.bench({ 78 | name: "v4.2.0", 79 | fn() { 80 | repeats.forEach(() => asUnoptional420(predicate)); 81 | }, 82 | group: "asUnoptional (predicate)", 83 | }); 84 | 85 | Deno.bench({ 86 | name: "v4.2.0", 87 | fn() { 88 | repeats.forEach(() => asUnoptional420(optional)); 89 | }, 90 | group: "asUnoptional (optional)", 91 | }); 92 | 93 | Deno.bench({ 94 | name: "v4.2.0", 95 | fn() { 96 | repeats.forEach(() => hasOptional420(predicate)); 97 | }, 98 | group: "hasOptional (predicate)", 99 | }); 100 | 101 | Deno.bench({ 102 | name: "v4.2.0", 103 | fn() { 104 | repeats.forEach(() => hasOptional420(optional)); 105 | }, 106 | group: "hasOptional (optional)", 107 | }); 108 | -------------------------------------------------------------------------------- /as/readonly.ts: -------------------------------------------------------------------------------- 1 | import { rewriteName } from "../_funcutil.ts"; 2 | import type { Predicate } from "../type.ts"; 3 | import { 4 | annotate, 5 | type AsReadonly, 6 | hasAnnotation, 7 | unannotate, 8 | } from "../_annotation.ts"; 9 | 10 | /** 11 | * Annotate the given predicate function as readonly. 12 | * 13 | * Use this function to annotate a predicate function of `predObj` in {@linkcode [is/object-of].isObjectOf|isObjectOf}. 14 | * 15 | * Use {@linkcode asUnreadonly} to remove the annotation. 16 | * Use {@linkcode hasReadonly} to check if a predicate function has annotated with this function. 17 | * 18 | * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. 19 | * 20 | * ```ts 21 | * import { as, is } from "@core/unknownutil"; 22 | * 23 | * const isMyType = is.ObjectOf({ 24 | * foo: as.Readonly(is.String), 25 | * }); 26 | * const a: unknown = {}; 27 | * if (isMyType(a)) { 28 | * const _: {readonly foo: string} = a; 29 | * } 30 | * ``` 31 | */ 32 | export function asReadonly

>( 33 | pred: P, 34 | ): P & AsReadonly { 35 | if (hasAnnotation(pred, "readonly")) { 36 | return pred as P & AsReadonly; 37 | } 38 | return rewriteName( 39 | annotate((x) => pred(x), "readonly", pred), 40 | "asReadonly", 41 | pred, 42 | ) as unknown as P & AsReadonly; 43 | } 44 | 45 | /** 46 | * Unannotate the annotated predicate function with {@linkcode asReadonly}. 47 | * 48 | * Use this function to unannotate a predicate function of `predObj` in {@linkcode [is/object-of].isObjectOf|isObjectOf}. 49 | * 50 | * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. 51 | * 52 | * ```ts 53 | * import { as, is } from "@core/unknownutil"; 54 | * 55 | * const isMyType = is.ObjectOf({ 56 | * foo: as.Unreadonly(as.Readonly(is.String)), 57 | * }); 58 | * const a: unknown = {foo: "a"}; 59 | * if (isMyType(a)) { 60 | * const _: {foo: string} = a; 61 | * } 62 | * ``` 63 | */ 64 | export function asUnreadonly< 65 | P extends Predicate, 66 | T extends P extends Predicate ? T : never, 67 | >(pred: P): Predicate { 68 | if (!hasAnnotation(pred, "readonly")) { 69 | return pred as Predicate; 70 | } 71 | return unannotate(pred, "readonly") as Predicate; 72 | } 73 | 74 | /** 75 | * Check if the given type predicate has readonly annotation. 76 | */ 77 | export function hasReadonly< 78 | P extends Predicate, 79 | >( 80 | pred: P, 81 | ): pred is P & AsReadonly { 82 | return hasAnnotation(pred, "readonly"); 83 | } 84 | -------------------------------------------------------------------------------- /as/readonly_bench.ts: -------------------------------------------------------------------------------- 1 | import { 2 | asReadonly as asReadonly420, 3 | asUnreadonly as asUnreadonly420, 4 | hasReadonly as hasReadonly420, 5 | } from "jsr:@core/unknownutil@4.2.0/as/readonly"; 6 | import { asReadonly, asUnreadonly, hasReadonly } from "./readonly.ts"; 7 | 8 | const repeats = Array.from({ length: 100 }); 9 | 10 | const predicate = (x: unknown): x is string => typeof x === "string"; 11 | const readonly = asReadonly((x: unknown): x is string => typeof x === "string"); 12 | 13 | Deno.bench({ 14 | name: "current", 15 | fn() { 16 | repeats.forEach(() => asReadonly(predicate)); 17 | }, 18 | group: "asReadonly (predicate)", 19 | }); 20 | 21 | Deno.bench({ 22 | name: "current", 23 | fn() { 24 | repeats.forEach(() => asReadonly(readonly)); 25 | }, 26 | group: "asReadonly (readonly)", 27 | }); 28 | 29 | Deno.bench({ 30 | name: "current", 31 | fn() { 32 | repeats.forEach(() => asUnreadonly(predicate)); 33 | }, 34 | group: "asUnreadonly (predicate)", 35 | }); 36 | 37 | Deno.bench({ 38 | name: "current", 39 | fn() { 40 | repeats.forEach(() => asUnreadonly(readonly)); 41 | }, 42 | group: "asUnreadonly (readonly)", 43 | }); 44 | 45 | Deno.bench({ 46 | name: "current", 47 | fn() { 48 | repeats.forEach(() => hasReadonly(predicate)); 49 | }, 50 | group: "hasReadonly (predicate)", 51 | }); 52 | 53 | Deno.bench({ 54 | name: "current", 55 | fn() { 56 | repeats.forEach(() => hasReadonly(readonly)); 57 | }, 58 | group: "hasReadonly (readonly)", 59 | }); 60 | 61 | Deno.bench({ 62 | name: "v4.2.0", 63 | fn() { 64 | repeats.forEach(() => asReadonly420(predicate)); 65 | }, 66 | group: "asReadonly (predicate)", 67 | }); 68 | 69 | Deno.bench({ 70 | name: "v4.2.0", 71 | fn() { 72 | repeats.forEach(() => asReadonly420(readonly)); 73 | }, 74 | group: "asReadonly (readonly)", 75 | }); 76 | 77 | Deno.bench({ 78 | name: "v4.2.0", 79 | fn() { 80 | repeats.forEach(() => asUnreadonly420(predicate)); 81 | }, 82 | group: "asUnreadonly (predicate)", 83 | }); 84 | 85 | Deno.bench({ 86 | name: "v4.2.0", 87 | fn() { 88 | repeats.forEach(() => asUnreadonly420(readonly)); 89 | }, 90 | group: "asUnreadonly (readonly)", 91 | }); 92 | 93 | Deno.bench({ 94 | name: "v4.2.0", 95 | fn() { 96 | repeats.forEach(() => hasReadonly420(predicate)); 97 | }, 98 | group: "hasReadonly (predicate)", 99 | }); 100 | 101 | Deno.bench({ 102 | name: "v4.2.0", 103 | fn() { 104 | repeats.forEach(() => hasReadonly420(readonly)); 105 | }, 106 | group: "hasReadonly (readonly)", 107 | }); 108 | -------------------------------------------------------------------------------- /assert.ts: -------------------------------------------------------------------------------- 1 | import type { Predicate } from "./type.ts"; 2 | 3 | /** 4 | * A factory function that generates assertion error messages. 5 | */ 6 | export type AssertMessageFactory = ( 7 | x: unknown, 8 | pred: Predicate, 9 | name?: string, 10 | ) => string; 11 | 12 | /** 13 | * The default factory function used to generate assertion error messages. 14 | */ 15 | export const defaultAssertMessageFactory: AssertMessageFactory = ( 16 | x, 17 | pred, 18 | name, 19 | ) => { 20 | const p = pred.name || "anonymous predicate"; 21 | const t = typeof x; 22 | const v = JSON.stringify(x, null, 2); 23 | return `Expected ${ 24 | name ?? "a value" 25 | } that satisfies the predicate ${p}, got ${t}: ${v}`; 26 | }; 27 | 28 | let assertMessageFactory = defaultAssertMessageFactory; 29 | 30 | /** 31 | * Represents an error that occurs when an assertion fails. 32 | */ 33 | export class AssertError extends Error { 34 | /** 35 | * Constructs a new instance. 36 | * @param message The error message. 37 | */ 38 | constructor(message?: string) { 39 | super(message); 40 | 41 | if (Error.captureStackTrace) { 42 | Error.captureStackTrace(this, AssertError); 43 | } 44 | 45 | this.name = this.constructor.name; 46 | } 47 | } 48 | 49 | /** 50 | * Sets the factory function used to generate assertion error messages. 51 | * 52 | * ```ts 53 | * import { is, setAssertMessageFactory } from "@core/unknownutil"; 54 | * 55 | * setAssertMessageFactory((x, pred) => { 56 | * if (pred === is.String) { 57 | * return `Expected a string, got ${typeof x}`; 58 | * } else if (pred === is.Number) { 59 | * return `Expected a number, got ${typeof x}`; 60 | * } else if (pred === is.Boolean) { 61 | * return `Expected a boolean, got ${typeof x}`; 62 | * } else { 63 | * return `Expected a value that satisfies the predicate, got ${typeof x}`; 64 | * } 65 | * }); 66 | * ``` 67 | * 68 | * @param factory The factory function. 69 | */ 70 | export function setAssertMessageFactory(factory: AssertMessageFactory): void { 71 | assertMessageFactory = factory; 72 | } 73 | 74 | /** 75 | * Asserts that the given value satisfies the provided predicate. 76 | * 77 | * It throws {@linkcode AssertError} if the value does not satisfy the predicate. 78 | * 79 | * ```ts 80 | * import { assert, is } from "@core/unknownutil"; 81 | * 82 | * const a: unknown = "hello"; 83 | * assert(a, is.String); 84 | * // a is now narrowed to string 85 | * ``` 86 | * 87 | * @param x The value to be asserted. 88 | * @param pred The predicate function to test the value against. 89 | * @param options Optional configuration for the assertion. 90 | * @returns The function has a return type of `asserts x is T` to help TypeScript narrow down the type of `x` after the assertion. 91 | */ 92 | export function assert( 93 | x: unknown, 94 | pred: Predicate, 95 | options: { message?: string; name?: string } = {}, 96 | ): asserts x is T { 97 | if (!pred(x)) { 98 | throw new AssertError( 99 | options.message ?? assertMessageFactory(x, pred, options.name), 100 | ); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /assert_test.ts: -------------------------------------------------------------------------------- 1 | import { assertThrows } from "@std/assert"; 2 | import { 3 | assert, 4 | AssertError, 5 | defaultAssertMessageFactory, 6 | setAssertMessageFactory, 7 | } from "./assert.ts"; 8 | 9 | const x: unknown = Symbol("x"); 10 | 11 | function truePredicate(_x: unknown): _x is string { 12 | return true; 13 | } 14 | 15 | function falsePredicate(_x: unknown): _x is string { 16 | return false; 17 | } 18 | 19 | Deno.test("assert", async (t) => { 20 | await t.step("does nothing on true predicate", () => { 21 | assert(x, truePredicate); 22 | }); 23 | 24 | await t.step("throws an `AssertError` on false predicate", () => { 25 | assertThrows( 26 | () => assert(x, falsePredicate), 27 | AssertError, 28 | `Expected a value that satisfies the predicate falsePredicate, got symbol: undefined`, 29 | ); 30 | }); 31 | 32 | await t.step( 33 | "throws an `AssertError` on false predicate with an anonymous predicate", 34 | () => { 35 | assertThrows( 36 | () => assert(x, (_x: unknown): _x is string => false), 37 | AssertError, 38 | `Expected a value that satisfies the predicate anonymous predicate, got symbol: undefined`, 39 | ); 40 | }, 41 | ); 42 | 43 | await t.step( 44 | "throws an `AssertError` on false predicate with a custom name", 45 | () => { 46 | assertThrows( 47 | () => assert(x, falsePredicate, { name: "hello world" }), 48 | AssertError, 49 | `Expected hello world that satisfies the predicate falsePredicate, got symbol: undefined`, 50 | ); 51 | }, 52 | ); 53 | 54 | await t.step( 55 | "throws an `AssertError` with a custom message on false predicate", 56 | () => { 57 | assertThrows( 58 | () => assert(x, falsePredicate, { message: "Hello" }), 59 | AssertError, 60 | "Hello", 61 | ); 62 | }, 63 | ); 64 | }); 65 | 66 | Deno.test("setAssertMessageFactory", async (t) => { 67 | setAssertMessageFactory((x, pred) => `Hello ${typeof x} ${pred.name}`); 68 | 69 | await t.step("change `AssertError` message on `assert` failure", () => { 70 | assertThrows( 71 | () => assert(x, falsePredicate), 72 | AssertError, 73 | "Hello symbol falsePredicate", 74 | ); 75 | }); 76 | 77 | setAssertMessageFactory(defaultAssertMessageFactory); 78 | }); 79 | -------------------------------------------------------------------------------- /deno.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@core/unknownutil", 3 | "version": "0.0.0", 4 | "compilerOptions": { 5 | "exactOptionalPropertyTypes": true 6 | }, 7 | "exports": { 8 | ".": "./mod.ts", 9 | "./as": "./as/mod.ts", 10 | "./as/optional": "./as/optional.ts", 11 | "./as/readonly": "./as/readonly.ts", 12 | "./assert": "./assert.ts", 13 | "./ensure": "./ensure.ts", 14 | "./is": "./is/mod.ts", 15 | "./is/any": "./is/any.ts", 16 | "./is/array": "./is/array.ts", 17 | "./is/array-of": "./is/array_of.ts", 18 | "./is/async-function": "./is/async_function.ts", 19 | "./is/bigint": "./is/bigint.ts", 20 | "./is/boolean": "./is/boolean.ts", 21 | "./is/custom-jsonable": "./is/custom_jsonable.ts", 22 | "./is/function": "./is/function.ts", 23 | "./is/instance-of": "./is/instance_of.ts", 24 | "./is/intersection-of": "./is/intersection_of.ts", 25 | "./is/jsonable": "./is/jsonable.ts", 26 | "./is/literal-of": "./is/literal_of.ts", 27 | "./is/literal-one-of": "./is/literal_one_of.ts", 28 | "./is/map": "./is/map.ts", 29 | "./is/map-of": "./is/map_of.ts", 30 | "./is/null": "./is/null.ts", 31 | "./is/nullish": "./is/nullish.ts", 32 | "./is/number": "./is/number.ts", 33 | "./is/object-of": "./is/object_of.ts", 34 | "./is/omit-of": "./is/omit_of.ts", 35 | "./is/parameters-of": "./is/parameters_of.ts", 36 | "./is/partial-of": "./is/partial_of.ts", 37 | "./is/pick-of": "./is/pick_of.ts", 38 | "./is/primitive": "./is/primitive.ts", 39 | "./is/readonly-of": "./is/readonly_of.ts", 40 | "./is/record": "./is/record.ts", 41 | "./is/record-object": "./is/record_object.ts", 42 | "./is/record-object-of": "./is/record_object_of.ts", 43 | "./is/record-of": "./is/record_of.ts", 44 | "./is/required-of": "./is/required_of.ts", 45 | "./is/set": "./is/set.ts", 46 | "./is/set-of": "./is/set_of.ts", 47 | "./is/strict-of": "./is/strict_of.ts", 48 | "./is/string": "./is/string.ts", 49 | "./is/symbol": "./is/symbol.ts", 50 | "./is/sync-function": "./is/sync_function.ts", 51 | "./is/tuple-of": "./is/tuple_of.ts", 52 | "./is/undefined": "./is/undefined.ts", 53 | "./is/uniform-tuple-of": "./is/uniform_tuple_of.ts", 54 | "./is/union-of": "./is/union_of.ts", 55 | "./is/unknown": "./is/unknown.ts", 56 | "./maybe": "./maybe.ts", 57 | "./type": "./type.ts" 58 | }, 59 | "exclude": [ 60 | ".coverage/**" 61 | ], 62 | "publish": { 63 | "include": [ 64 | "**/*.ts", 65 | "README.md", 66 | "LICENSE" 67 | ], 68 | "exclude": [ 69 | "**/*_bench.ts", 70 | "**/*_test.ts", 71 | ".*" 72 | ] 73 | }, 74 | "imports": { 75 | "@core/iterutil": "jsr:@core/iterutil@^0.3.0", 76 | "@core/unknownutil": "./mod.ts", 77 | "@deno/dnt": "jsr:@deno/dnt@^0.41.1", 78 | "@deno/doc": "jsr:@deno/doc@^0.153.0", 79 | "@std/assert": "jsr:@std/assert@^1.0.4", 80 | "@std/jsonc": "jsr:@std/jsonc@^1.0.0", 81 | "@std/path": "jsr:@std/path@^1.0.2", 82 | "@std/testing": "jsr:@std/testing@^1.0.2" 83 | }, 84 | "tasks": { 85 | "check": "deno check **/*.ts", 86 | "test": "deno test -A --doc --parallel --shuffle", 87 | "test:coverage": "deno task test --coverage=.coverage", 88 | "coverage": "deno coverage .coverage", 89 | "gen": "deno run --allow-net=jsr.io --allow-env --allow-read --allow-write=. .scripts/gen-mod.ts", 90 | "update": "deno run --allow-env --allow-read --allow-write=. --allow-run=git,deno --allow-net=jsr.io,registry.npmjs.org jsr:@molt/cli ./*.ts", 91 | "update:commit": "deno task -q update --commit --prefix deps: --pre-commit=fmt,lint" 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /ensure.ts: -------------------------------------------------------------------------------- 1 | import type { Predicate } from "./type.ts"; 2 | import { assert } from "./assert.ts"; 3 | 4 | /** 5 | * Ensures that the given value satisfies the provided predicate. 6 | * 7 | * It throws {@linkcode [assert].AssertError|AssertError} if the value does not satisfy the predicate. 8 | * 9 | * ```ts 10 | * import { ensure, is } from "@core/unknownutil"; 11 | * 12 | * const a: unknown = "hello"; 13 | * const _: string = ensure(a, is.String); 14 | * ``` 15 | * 16 | * @param x The value to be ensured. 17 | * @param pred The predicate function to test the value against. 18 | * @param options Optional configuration for the assertion. 19 | * @returns The input value `x`. 20 | */ 21 | export function ensure( 22 | x: unknown, 23 | pred: Predicate, 24 | options: { message?: string; name?: string } = {}, 25 | ): T { 26 | assert(x, pred, options); 27 | return x; 28 | } 29 | -------------------------------------------------------------------------------- /ensure_test.ts: -------------------------------------------------------------------------------- 1 | import { assertStrictEquals, assertThrows } from "@std/assert"; 2 | import { 3 | AssertError, 4 | defaultAssertMessageFactory, 5 | setAssertMessageFactory, 6 | } from "./assert.ts"; 7 | import { ensure } from "./ensure.ts"; 8 | 9 | const x: unknown = Symbol("x"); 10 | 11 | function truePredicate(_x: unknown): _x is string { 12 | return true; 13 | } 14 | 15 | function falsePredicate(_x: unknown): _x is string { 16 | return false; 17 | } 18 | 19 | Deno.test("ensure", async (t) => { 20 | await t.step("returns `x` as-is on true predicate", () => { 21 | assertStrictEquals(ensure(x, truePredicate), x); 22 | }); 23 | 24 | await t.step("throws an `AssertError` on false predicate", () => { 25 | assertThrows( 26 | () => ensure(x, falsePredicate), 27 | AssertError, 28 | `Expected a value that satisfies the predicate falsePredicate, got symbol: undefined`, 29 | ); 30 | }); 31 | 32 | await t.step( 33 | "throws an `AssertError` on false predicate with a custom name", 34 | () => { 35 | assertThrows( 36 | () => ensure(x, falsePredicate, { name: "hello world" }), 37 | AssertError, 38 | `Expected hello world that satisfies the predicate falsePredicate, got symbol: undefined`, 39 | ); 40 | }, 41 | ); 42 | 43 | await t.step( 44 | "throws an `AssertError` with a custom message on false predicate", 45 | () => { 46 | assertThrows( 47 | () => ensure(x, falsePredicate, { message: "Hello" }), 48 | AssertError, 49 | "Hello", 50 | ); 51 | }, 52 | ); 53 | }); 54 | 55 | Deno.test("setAssertMessageFactory", async (t) => { 56 | setAssertMessageFactory((x, pred) => `Hello ${typeof x} ${pred.name}`); 57 | 58 | await t.step("change `AssertError` message on `ensure` failure", () => { 59 | assertThrows( 60 | () => ensure(x, falsePredicate), 61 | AssertError, 62 | "Hello symbol falsePredicate", 63 | ); 64 | }); 65 | 66 | setAssertMessageFactory(defaultAssertMessageFactory); 67 | }); 68 | -------------------------------------------------------------------------------- /is/__snapshots__/intersection_of_test.ts.snap: -------------------------------------------------------------------------------- 1 | export const snapshot = {}; 2 | 3 | snapshot[`isIntersectionOf > returns properly named predicate function 1`] = `"isString"`; 4 | 5 | snapshot[`isIntersectionOf > returns properly named predicate function 2`] = ` 6 | "isObjectOf({ 7 | a: isNumber, 8 | b: isString 9 | })" 10 | `; 11 | 12 | snapshot[`isIntersectionOf > returns properly named predicate function 3`] = ` 13 | "isIntersectionOf([ 14 | isFunction, 15 | isObjectOf({b: isString}) 16 | ])" 17 | `; 18 | 19 | snapshot[`isIntersectionOf > with symbol properties > returns properly named predicate function 1`] = `"isString"`; 20 | 21 | snapshot[`isIntersectionOf > with symbol properties > returns properly named predicate function 2`] = ` 22 | "isObjectOf({ 23 | a: isNumber, 24 | Symbol(b): isString 25 | })" 26 | `; 27 | 28 | snapshot[`isIntersectionOf > with symbol properties > returns properly named predicate function 3`] = ` 29 | "isIntersectionOf([ 30 | isFunction, 31 | isObjectOf({Symbol(b): isString}) 32 | ])" 33 | `; 34 | -------------------------------------------------------------------------------- /is/__snapshots__/object_of_test.ts.snap: -------------------------------------------------------------------------------- 1 | export const snapshot = {}; 2 | 3 | snapshot[`isObjectOf > returns properly named predicate function 1`] = ` 4 | "isObjectOf({ 5 | a: isNumber, 6 | b: isString, 7 | c: isBoolean 8 | })" 9 | `; 10 | 11 | snapshot[`isObjectOf > returns properly named predicate function 2`] = `"isObjectOf({a: a})"`; 12 | 13 | snapshot[`isObjectOf > returns properly named predicate function 3`] = ` 14 | "isObjectOf({ 15 | a: isObjectOf({ 16 | b: isObjectOf({c: isBoolean}) 17 | }) 18 | })" 19 | `; 20 | 21 | snapshot[`isObjectOf > with symbol properties > returns properly named predicate function 1`] = ` 22 | "isObjectOf({ 23 | a: isNumber, 24 | b: isString, 25 | Symbol(s): isBoolean 26 | })" 27 | `; 28 | 29 | snapshot[`isObjectOf > with symbol properties > returns properly named predicate function 2`] = ` 30 | "isObjectOf({ 31 | Symbol(a): isObjectOf({ 32 | Symbol(b): isObjectOf({Symbol(c): isBoolean}) 33 | }) 34 | })" 35 | `; 36 | -------------------------------------------------------------------------------- /is/__snapshots__/omit_of_test.ts.snap: -------------------------------------------------------------------------------- 1 | export const snapshot = {}; 2 | 3 | snapshot[`isOmitOf > returns properly named predicate function 1`] = ` 4 | "isObjectOf({ 5 | a: isNumber, 6 | c: isBoolean 7 | })" 8 | `; 9 | 10 | snapshot[`isOmitOf > returns properly named predicate function 2`] = `"isObjectOf({a: isNumber})"`; 11 | 12 | snapshot[`isOmitOf > with symbol properties > returns properly named predicate function 1`] = ` 13 | "isObjectOf({ 14 | a: isNumber, 15 | Symbol(c): isBoolean 16 | })" 17 | `; 18 | 19 | snapshot[`isOmitOf > with symbol properties > returns properly named predicate function 2`] = `"isObjectOf({a: isNumber})"`; 20 | -------------------------------------------------------------------------------- /is/__snapshots__/parameters_of_test.ts.snap: -------------------------------------------------------------------------------- 1 | export const snapshot = {}; 2 | 3 | snapshot[`isParametersOf > returns properly named predicate function 1`] = ` 4 | "isParametersOf([ 5 | isNumber, 6 | isString, 7 | asOptional(isBoolean) 8 | ])" 9 | `; 10 | 11 | snapshot[`isParametersOf > returns properly named predicate function 2`] = `"isParametersOf([(anonymous)])"`; 12 | 13 | snapshot[`isParametersOf > returns properly named predicate function 3`] = `"isParametersOf([])"`; 14 | 15 | snapshot[`isParametersOf > returns properly named predicate function 4`] = ` 16 | "isParametersOf([ 17 | isParametersOf([ 18 | isParametersOf([ 19 | isNumber, 20 | isString, 21 | asOptional(isBoolean) 22 | ]) 23 | ]) 24 | ])" 25 | `; 26 | 27 | snapshot[`isParametersOf > returns properly named predicate function 1`] = ` 28 | "isParametersOf([ 29 | isNumber, 30 | isString, 31 | asOptional(isBoolean) 32 | ], isArray)" 33 | `; 34 | 35 | snapshot[`isParametersOf > returns properly named predicate function 2`] = `"isParametersOf([(anonymous)], isArrayOf(isString))"`; 36 | 37 | snapshot[`isParametersOf > returns properly named predicate function 3`] = `"isParametersOf([], isArrayOf(isString))"`; 38 | 39 | snapshot[`isParametersOf > returns properly named predicate function 4`] = ` 40 | "isParametersOf([ 41 | isParametersOf([ 42 | isParametersOf([ 43 | isNumber, 44 | isString, 45 | asOptional(isBoolean) 46 | ], isArray) 47 | ], isArray) 48 | ])" 49 | `; 50 | -------------------------------------------------------------------------------- /is/__snapshots__/partial_of_test.ts.snap: -------------------------------------------------------------------------------- 1 | export const snapshot = {}; 2 | 3 | snapshot[`isPartialOf > returns properly named predicate function 1`] = ` 4 | "isObjectOf({ 5 | a: asOptional(isNumber), 6 | b: asOptional(isUnionOf([ 7 | isString, 8 | isUndefined 9 | ])), 10 | c: asOptional(isBoolean), 11 | d: asOptional(asReadonly(isString)) 12 | })" 13 | `; 14 | 15 | snapshot[`isPartialOf > returns properly named predicate function 2`] = ` 16 | "isObjectOf({ 17 | a: asOptional(isNumber), 18 | b: asOptional(isUnionOf([ 19 | isString, 20 | isUndefined 21 | ])), 22 | c: asOptional(isBoolean), 23 | d: asOptional(asReadonly(isString)) 24 | })" 25 | `; 26 | 27 | snapshot[`isPartialOf > with symbol properties > returns properly named predicate function 1`] = ` 28 | "isObjectOf({ 29 | a: asOptional(isNumber), 30 | Symbol(b): asOptional(isUnionOf([ 31 | isString, 32 | isUndefined 33 | ])), 34 | Symbol(c): asOptional(isBoolean), 35 | Symbol(c): asOptional(asReadonly(isString)) 36 | })" 37 | `; 38 | 39 | snapshot[`isPartialOf > with symbol properties > returns properly named predicate function 2`] = ` 40 | "isObjectOf({ 41 | a: asOptional(isNumber), 42 | Symbol(b): asOptional(isUnionOf([ 43 | isString, 44 | isUndefined 45 | ])), 46 | Symbol(c): asOptional(isBoolean), 47 | Symbol(c): asOptional(asReadonly(isString)) 48 | })" 49 | `; 50 | -------------------------------------------------------------------------------- /is/__snapshots__/pick_of_test.ts.snap: -------------------------------------------------------------------------------- 1 | export const snapshot = {}; 2 | 3 | snapshot[`isPickOf > returns properly named predicate function 1`] = ` 4 | "isObjectOf({ 5 | a: isNumber, 6 | c: isBoolean 7 | })" 8 | `; 9 | 10 | snapshot[`isPickOf > returns properly named predicate function 2`] = `"isObjectOf({a: isNumber})"`; 11 | 12 | snapshot[`isPickOf > with symbol properties > returns properly named predicate function 1`] = ` 13 | "isObjectOf({ 14 | a: isNumber, 15 | Symbol(c): isBoolean 16 | })" 17 | `; 18 | 19 | snapshot[`isPickOf > with symbol properties > returns properly named predicate function 2`] = `"isObjectOf({Symbol(c): isBoolean})"`; 20 | -------------------------------------------------------------------------------- /is/__snapshots__/readonly_of_test.ts.snap: -------------------------------------------------------------------------------- 1 | export const snapshot = {}; 2 | 3 | snapshot[`isReadonlyOf > with isRecord > returns properly named predicate function 1`] = `"isReadonlyOf(isRecord)"`; 4 | 5 | snapshot[`isReadonlyOf > with isRecord > returns properly named predicate function 2`] = `"isReadonlyOf(isRecord)"`; 6 | 7 | snapshot[`isReadonlyOf > with isObjectOf > returns properly named predicate function 1`] = ` 8 | "isReadonlyOf(isObjectOf({ 9 | a: isNumber, 10 | b: isUnionOf([ 11 | isString, 12 | isUndefined 13 | ]), 14 | c: asReadonly(isBoolean) 15 | }))" 16 | `; 17 | 18 | snapshot[`isReadonlyOf > with isObjectOf > returns properly named predicate function 2`] = ` 19 | "isReadonlyOf(isObjectOf({ 20 | a: isNumber, 21 | b: isUnionOf([ 22 | isString, 23 | isUndefined 24 | ]), 25 | c: asReadonly(isBoolean) 26 | }))" 27 | `; 28 | 29 | snapshot[`isReadonlyOf > with isTupleOf > returns properly named predicate function 1`] = ` 30 | "isReadonlyOf(isTupleOf([ 31 | isNumber, 32 | isString, 33 | asReadonly(isBoolean) 34 | ]))" 35 | `; 36 | 37 | snapshot[`isReadonlyOf > with isTupleOf > returns properly named predicate function 2`] = ` 38 | "isReadonlyOf(isTupleOf([ 39 | isNumber, 40 | isString, 41 | asReadonly(isBoolean) 42 | ]))" 43 | `; 44 | 45 | snapshot[`isReadonlyOf > with isUniformTupleOf > returns properly named predicate function 1`] = `"isReadonlyOf(isUniformTupleOf(3, isNumber))"`; 46 | 47 | snapshot[`isReadonlyOf > with isUniformTupleOf > returns properly named predicate function 2`] = `"isReadonlyOf(isUniformTupleOf(3, isNumber))"`; 48 | 49 | snapshot[`isReadonlyOf > with symbol properties > with isObjectOf > returns properly named predicate function 1`] = ` 50 | "isReadonlyOf(isObjectOf({ 51 | a: isNumber, 52 | Symbol(b): isUnionOf([ 53 | isString, 54 | isUndefined 55 | ]), 56 | Symbol(c): asReadonly(isBoolean) 57 | }))" 58 | `; 59 | 60 | snapshot[`isReadonlyOf > with symbol properties > with isObjectOf > returns properly named predicate function 2`] = ` 61 | "isReadonlyOf(isObjectOf({ 62 | a: isNumber, 63 | Symbol(b): isUnionOf([ 64 | isString, 65 | isUndefined 66 | ]), 67 | Symbol(c): asReadonly(isBoolean) 68 | }))" 69 | `; 70 | -------------------------------------------------------------------------------- /is/__snapshots__/record_object_of_test.ts.snap: -------------------------------------------------------------------------------- 1 | export const snapshot = {}; 2 | 3 | snapshot[`isRecordObjectOf > returns properly named predicate function 1`] = `"isRecordObjectOf(isNumber, undefined)"`; 4 | 5 | snapshot[`isRecordObjectOf > returns properly named predicate function 2`] = `"isRecordObjectOf((anonymous), undefined)"`; 6 | 7 | snapshot[`isRecordObjectOf > returns properly named predicate function 1`] = `"isRecordObjectOf(isNumber, isString)"`; 8 | 9 | snapshot[`isRecordObjectOf > returns properly named predicate function 2`] = `"isRecordObjectOf((anonymous), isString)"`; 10 | 11 | snapshot[`isRecordObjectOf > with symbol properties > returns properly named predicate function 1`] = `"isRecordObjectOf(isNumber, isSymbol)"`; 12 | 13 | snapshot[`isRecordObjectOf > with symbol properties > returns properly named predicate function 2`] = `"isRecordObjectOf((anonymous), isSymbol)"`; 14 | -------------------------------------------------------------------------------- /is/__snapshots__/record_of_test.ts.snap: -------------------------------------------------------------------------------- 1 | export const snapshot = {}; 2 | 3 | snapshot[`isRecordOf > returns properly named predicate function 1`] = `"isRecordOf(isNumber, undefined)"`; 4 | 5 | snapshot[`isRecordOf > returns properly named predicate function 2`] = `"isRecordOf((anonymous), undefined)"`; 6 | 7 | snapshot[`isRecordOf > returns properly named predicate function 1`] = `"isRecordOf(isNumber, isString)"`; 8 | 9 | snapshot[`isRecordOf > returns properly named predicate function 2`] = `"isRecordOf((anonymous), isString)"`; 10 | 11 | snapshot[`isRecordOf > with symbol properties > returns properly named predicate function 1`] = `"isRecordOf(isNumber, isSymbol)"`; 12 | 13 | snapshot[`isRecordOf > with symbol properties > returns properly named predicate function 2`] = `"isRecordOf((anonymous), isSymbol)"`; 14 | -------------------------------------------------------------------------------- /is/__snapshots__/required_of_test.ts.snap: -------------------------------------------------------------------------------- 1 | export const snapshot = {}; 2 | 3 | snapshot[`isRequiredOf > returns properly named predicate function 1`] = ` 4 | "isObjectOf({ 5 | a: isNumber, 6 | b: isUnionOf([ 7 | isString, 8 | isUndefined 9 | ]), 10 | c: isBoolean, 11 | d: asReadonly(isString) 12 | })" 13 | `; 14 | 15 | snapshot[`isRequiredOf > returns properly named predicate function 2`] = ` 16 | "isObjectOf({ 17 | a: isNumber, 18 | b: isUnionOf([ 19 | isString, 20 | isUndefined 21 | ]), 22 | c: isBoolean, 23 | d: asReadonly(isString) 24 | })" 25 | `; 26 | 27 | snapshot[`isRequiredOf > with symbol properties > returns properly named predicate function 1`] = ` 28 | "isObjectOf({ 29 | a: isNumber, 30 | Symbol(b): isUnionOf([ 31 | isString, 32 | isUndefined 33 | ]), 34 | Symbol(c): isBoolean, 35 | Symbol(d): asReadonly(isString) 36 | })" 37 | `; 38 | 39 | snapshot[`isRequiredOf > with symbol properties > returns properly named predicate function 2`] = ` 40 | "isObjectOf({ 41 | a: isNumber, 42 | Symbol(b): isUnionOf([ 43 | isString, 44 | isUndefined 45 | ]), 46 | Symbol(c): isBoolean, 47 | Symbol(d): asReadonly(isString) 48 | })" 49 | `; 50 | -------------------------------------------------------------------------------- /is/__snapshots__/set_of_test.ts.snap: -------------------------------------------------------------------------------- 1 | export const snapshot = {}; 2 | 3 | snapshot[`isSetOf > returns properly named predicate function 1`] = `"isSetOf(isNumber)"`; 4 | 5 | snapshot[`isSetOf > returns properly named predicate function 2`] = `"isSetOf((anonymous))"`; 6 | -------------------------------------------------------------------------------- /is/__snapshots__/strict_of_test.ts.snap: -------------------------------------------------------------------------------- 1 | export const snapshot = {}; 2 | 3 | snapshot[`isStrictOf > returns properly named predicate function 1`] = ` 4 | "isStrictOf(isObjectOf({ 5 | a: isNumber, 6 | b: isString, 7 | c: isBoolean 8 | }))" 9 | `; 10 | 11 | snapshot[`isStrictOf > returns properly named predicate function 2`] = `"isStrictOf(isObjectOf({a: a}))"`; 12 | 13 | snapshot[`isStrictOf > returns properly named predicate function 3`] = ` 14 | "isStrictOf(isObjectOf({ 15 | a: isStrictOf(isObjectOf({ 16 | b: isStrictOf(isObjectOf({c: isBoolean})) 17 | })) 18 | }))" 19 | `; 20 | 21 | snapshot[`isStrictOf > with symbol properties > returns properly named predicate function 1`] = ` 22 | "isStrictOf(isObjectOf({ 23 | a: isNumber, 24 | Symbol(b): isString, 25 | Symbol(c): isBoolean 26 | }))" 27 | `; 28 | 29 | snapshot[`isStrictOf > with symbol properties > returns properly named predicate function 2`] = ` 30 | "isStrictOf(isObjectOf({ 31 | a: isStrictOf(isObjectOf({ 32 | Symbol(b): isStrictOf(isObjectOf({Symbol(c): isBoolean})) 33 | })) 34 | }))" 35 | `; 36 | -------------------------------------------------------------------------------- /is/__snapshots__/tuple_of_test.ts.snap: -------------------------------------------------------------------------------- 1 | export const snapshot = {}; 2 | 3 | snapshot[`isTupleOf > returns properly named predicate function 1`] = ` 4 | "isTupleOf([ 5 | isNumber, 6 | isString, 7 | isBoolean 8 | ])" 9 | `; 10 | 11 | snapshot[`isTupleOf > returns properly named predicate function 2`] = `"isTupleOf([(anonymous)])"`; 12 | 13 | snapshot[`isTupleOf > returns properly named predicate function 3`] = ` 14 | "isTupleOf([ 15 | isTupleOf([ 16 | isTupleOf([ 17 | isNumber, 18 | isString, 19 | isBoolean 20 | ]) 21 | ]) 22 | ])" 23 | `; 24 | 25 | snapshot[`isTupleOf > returns properly named predicate function 1`] = ` 26 | "isTupleOf([ 27 | isNumber, 28 | isString, 29 | isBoolean 30 | ], isArray)" 31 | `; 32 | 33 | snapshot[`isTupleOf > returns properly named predicate function 2`] = `"isTupleOf([(anonymous)], isArrayOf(isString))"`; 34 | 35 | snapshot[`isTupleOf > returns properly named predicate function 3`] = ` 36 | "isTupleOf([ 37 | isTupleOf([ 38 | isTupleOf([ 39 | isNumber, 40 | isString, 41 | isBoolean 42 | ], isArrayOf(isString)) 43 | ], isArray) 44 | ])" 45 | `; 46 | 47 | snapshot[`isTupleOf > returns properly named predicate function 1`] = ` 48 | "isTupleOf(isArray, [ 49 | isNumber, 50 | isString, 51 | isBoolean 52 | ])" 53 | `; 54 | 55 | snapshot[`isTupleOf > returns properly named predicate function 2`] = `"isTupleOf(isArrayOf(isString), [(anonymous)])"`; 56 | 57 | snapshot[`isTupleOf > returns properly named predicate function 3`] = ` 58 | "isTupleOf([ 59 | isTupleOf(isArray, [ 60 | isTupleOf(isArrayOf(isString), [ 61 | isNumber, 62 | isString, 63 | isBoolean 64 | ]) 65 | ]) 66 | ])" 67 | `; 68 | 69 | snapshot[`isTupleOf > returns properly named predicate function 1`] = ` 70 | "isTupleOf([ 71 | isNumber, 72 | isString, 73 | isBoolean 74 | ], isArray, [ 75 | isString, 76 | isBoolean, 77 | isNumber 78 | ])" 79 | `; 80 | 81 | snapshot[`isTupleOf > returns properly named predicate function 2`] = `"isTupleOf([(anonymous)], isArrayOf(isString), [(anonymous)])"`; 82 | 83 | snapshot[`isTupleOf > returns properly named predicate function 3`] = ` 84 | "isTupleOf([ 85 | isTupleOf([ 86 | isTupleOf([ 87 | isNumber, 88 | isString, 89 | isBoolean 90 | ], isArrayOf(isString), [ 91 | isString, 92 | isBoolean, 93 | isNumber 94 | ]) 95 | ], isArray, [ 96 | isTupleOf([ 97 | isNumber, 98 | isString, 99 | isBoolean 100 | ], isArrayOf(isNumber), [ 101 | isNumber, 102 | isBoolean, 103 | isString 104 | ]) 105 | ]) 106 | ])" 107 | `; 108 | -------------------------------------------------------------------------------- /is/__snapshots__/uniform_tuple_of_test.ts.snap: -------------------------------------------------------------------------------- 1 | export const snapshot = {}; 2 | 3 | snapshot[`isUniformTupleOf > returns properly named predicate function 1`] = `"isUniformTupleOf(3, undefined)"`; 4 | 5 | snapshot[`isUniformTupleOf > returns properly named predicate function 2`] = `"isUniformTupleOf(3, isNumber)"`; 6 | 7 | snapshot[`isUniformTupleOf > returns properly named predicate function 3`] = `"isUniformTupleOf(3, (anonymous))"`; 8 | -------------------------------------------------------------------------------- /is/__snapshots__/union_of_test.ts.snap: -------------------------------------------------------------------------------- 1 | export const snapshot = {}; 2 | 3 | snapshot[`isUnionOf > returns properly named predicate function 1`] = ` 4 | "isUnionOf([ 5 | isNumber, 6 | isString, 7 | isBoolean 8 | ])" 9 | `; 10 | -------------------------------------------------------------------------------- /is/any.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Assume `x is `any` and always return `true` regardless of the type of `x`. 3 | * 4 | * Use {@linkcode [is/unknown].isUnknown|isUnknown} to assume that a value is `unknown`. 5 | * 6 | * ```ts 7 | * import { is } from "@core/unknownutil"; 8 | * 9 | * const a = "a"; 10 | * if (is.Any(a)) { 11 | * const _: any = a; 12 | * } 13 | * ``` 14 | */ 15 | // deno-lint-ignore no-explicit-any 16 | export function isAny(_x: unknown): _x is any { 17 | return true; 18 | } 19 | -------------------------------------------------------------------------------- /is/any_bench.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "@std/assert"; 2 | import { isAny as isAny420 } from "jsr:@core/unknownutil@4.2.0/is/any"; 3 | import { isAny } from "./any.ts"; 4 | 5 | const repeats = Array.from({ length: 100 }); 6 | const positive: unknown = ""; 7 | 8 | Deno.bench({ 9 | name: "current", 10 | fn() { 11 | assert(repeats.every(() => isAny(positive))); 12 | }, 13 | group: "isAny (positive)", 14 | }); 15 | 16 | Deno.bench({ 17 | name: "v4.2.0", 18 | fn() { 19 | assert(repeats.every(() => isAny420(positive))); 20 | }, 21 | group: "isAny (positive)", 22 | }); 23 | -------------------------------------------------------------------------------- /is/any_test.ts: -------------------------------------------------------------------------------- 1 | import { testWithExamples } from "../_testutil.ts"; 2 | import { isAny } from "./any.ts"; 3 | 4 | Deno.test("isAny", async (t) => { 5 | await testWithExamples(t, isAny, { 6 | validExamples: [ 7 | "string", 8 | "number", 9 | "bigint", 10 | "boolean", 11 | "array", 12 | "set", 13 | "record", 14 | "map", 15 | "syncFunction", 16 | "asyncFunction", 17 | "null", 18 | "undefined", 19 | "symbol", 20 | "date", 21 | "promise", 22 | ], 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /is/array.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Return `true` if the type of `x` is `unknown[]`. 3 | * 4 | * Use {@linkcode [is/arrayt-of].isArrayOf|isArrayOf} to check if the type of `x` is an array of `T`. 5 | * 6 | * ```ts 7 | * import { is } from "@core/unknownutil"; 8 | * 9 | * const a: unknown = [0, 1, 2]; 10 | * if (is.Array(a)) { 11 | * const _: unknown[] = a; 12 | * } 13 | * ``` 14 | */ 15 | export function isArray( 16 | x: unknown, 17 | ): x is unknown[] { 18 | return Array.isArray(x); 19 | } 20 | -------------------------------------------------------------------------------- /is/array_bench.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "@std/assert"; 2 | import { isArray as isArray420 } from "jsr:@core/unknownutil@4.2.0/is/array"; 3 | import { isArray } from "./array.ts"; 4 | 5 | const repeats = Array.from({ length: 100 }); 6 | const positive: unknown = []; 7 | const negative: unknown = ""; 8 | 9 | Deno.bench({ 10 | name: "current", 11 | fn() { 12 | assert(repeats.every(() => isArray(positive))); 13 | }, 14 | group: "isArray (positive)", 15 | }); 16 | 17 | Deno.bench({ 18 | name: "current", 19 | fn() { 20 | assert(repeats.every(() => !isArray(negative))); 21 | }, 22 | group: "isArray (negative)", 23 | }); 24 | 25 | Deno.bench({ 26 | name: "v4.2.0", 27 | fn() { 28 | assert(repeats.every(() => isArray420(positive))); 29 | }, 30 | group: "isArray (positive)", 31 | }); 32 | 33 | Deno.bench({ 34 | name: "v4.2.0", 35 | fn() { 36 | assert(repeats.every(() => !isArray420(negative))); 37 | }, 38 | group: "isArray (negative)", 39 | }); 40 | -------------------------------------------------------------------------------- /is/array_of.ts: -------------------------------------------------------------------------------- 1 | import { rewriteName } from "../_funcutil.ts"; 2 | import type { Predicate } from "../type.ts"; 3 | import { isArray } from "./array.ts"; 4 | 5 | /** 6 | * Return a type predicate function that returns `true` if the type of `x` is `T[]`. 7 | * 8 | * Use {@linkcode [is/array].isArray|isArray} to check if the type of `x` is an array of `unknown`. 9 | * 10 | * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. 11 | * 12 | * ```ts 13 | * import { is } from "@core/unknownutil"; 14 | * 15 | * const isMyType = is.ArrayOf(is.String); 16 | * const a: unknown = ["a", "b", "c"]; 17 | * if (isMyType(a)) { 18 | * const _: string[] = a; 19 | * } 20 | * ``` 21 | */ 22 | export function isArrayOf( 23 | pred: Predicate, 24 | ): Predicate { 25 | return rewriteName( 26 | (x: unknown): x is T[] => isArray(x) && x.every((v) => pred(v)), 27 | "isArrayOf", 28 | pred, 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /is/array_of_bench.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "@std/assert"; 2 | import { isArrayOf as isArrayOf420 } from "jsr:@core/unknownutil@4.2.0/is/array-of"; 3 | import { isArrayOf } from "./array_of.ts"; 4 | 5 | const repeats = Array.from({ length: 100 }); 6 | const positive: unknown = []; 7 | const positive10: unknown = Array.from({ length: 10 }).map(() => ""); 8 | const positive1000: unknown = Array.from({ length: 1000 }).map(() => ""); 9 | const negative: unknown = ""; 10 | const negative10: unknown = Array.from({ length: 10 }).map(() => 0); 11 | const negative1000: unknown = Array.from({ length: 1000 }).map(() => 0); 12 | 13 | const pred420 = isArrayOf420((x: unknown): x is string => 14 | typeof x === "string" 15 | ); 16 | const pred = isArrayOf((x: unknown): x is string => typeof x === "string"); 17 | 18 | Deno.bench({ 19 | name: "current", 20 | fn() { 21 | assert(repeats.every(() => pred(positive))); 22 | }, 23 | group: "isArrayOf (positive)", 24 | }); 25 | 26 | Deno.bench({ 27 | name: "current", 28 | fn() { 29 | assert(repeats.every(() => pred(positive10))); 30 | }, 31 | group: "isArrayOf (positive 10)", 32 | }); 33 | 34 | Deno.bench({ 35 | name: "current", 36 | fn() { 37 | assert(repeats.every(() => pred(positive1000))); 38 | }, 39 | group: "isArrayOf (positive 1000)", 40 | }); 41 | 42 | Deno.bench({ 43 | name: "current", 44 | fn() { 45 | assert(repeats.every(() => !pred(negative))); 46 | }, 47 | group: "isArrayOf (negative)", 48 | }); 49 | 50 | Deno.bench({ 51 | name: "current", 52 | fn() { 53 | assert(repeats.every(() => !pred(negative10))); 54 | }, 55 | group: "isArrayOf (negative 10)", 56 | }); 57 | 58 | Deno.bench({ 59 | name: "current", 60 | fn() { 61 | assert(repeats.every(() => !pred(negative1000))); 62 | }, 63 | group: "isArrayOf (negative 1000)", 64 | }); 65 | 66 | Deno.bench({ 67 | name: "v4.2.0", 68 | fn() { 69 | assert(repeats.every(() => pred420(positive))); 70 | }, 71 | group: "isArrayOf (positive)", 72 | }); 73 | 74 | Deno.bench({ 75 | name: "v4.2.0", 76 | fn() { 77 | assert(repeats.every(() => pred420(positive10))); 78 | }, 79 | group: "isArrayOf (positive 10)", 80 | }); 81 | 82 | Deno.bench({ 83 | name: "v4.2.0", 84 | fn() { 85 | assert(repeats.every(() => pred420(positive1000))); 86 | }, 87 | group: "isArrayOf (positive 1000)", 88 | }); 89 | 90 | Deno.bench({ 91 | name: "v4.2.0", 92 | fn() { 93 | assert(repeats.every(() => !pred420(negative))); 94 | }, 95 | group: "isArrayOf (negative)", 96 | }); 97 | 98 | Deno.bench({ 99 | name: "v4.2.0", 100 | fn() { 101 | assert(repeats.every(() => !pred420(negative10))); 102 | }, 103 | group: "isArrayOf (negative 10)", 104 | }); 105 | 106 | Deno.bench({ 107 | name: "v4.2.0", 108 | fn() { 109 | assert(repeats.every(() => !pred420(negative1000))); 110 | }, 111 | group: "isArrayOf (negative 1000)", 112 | }); 113 | -------------------------------------------------------------------------------- /is/array_of_test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from "@std/assert"; 2 | import { assertType, type IsExact } from "@std/testing/types"; 3 | import { is } from "./mod.ts"; 4 | import { isArrayOf } from "./array_of.ts"; 5 | 6 | Deno.test("isArrayOf", async (t) => { 7 | await t.step("returns properly named predicate function", () => { 8 | assertEquals(typeof isArrayOf(is.Number), "function"); 9 | assertEquals(isArrayOf(is.Number).name, "isArrayOf(isNumber)"); 10 | assertEquals( 11 | isArrayOf((_x): _x is unknown => true).name, 12 | "isArrayOf((anonymous))", 13 | ); 14 | }); 15 | 16 | await t.step("returns true on T array", () => { 17 | assertEquals(isArrayOf(is.Number)([0, 1, 2]), true); 18 | assertEquals(isArrayOf(is.String)(["a", "b", "c"]), true); 19 | assertEquals(isArrayOf(is.Boolean)([true, false, true]), true); 20 | }); 21 | 22 | await t.step("returns false on non T array", () => { 23 | assertEquals(isArrayOf(is.String)([0, 1, 2]), false); 24 | assertEquals(isArrayOf(is.Number)(["a", "b", "c"]), false); 25 | assertEquals(isArrayOf(is.String)([true, false, true]), false); 26 | }); 27 | 28 | await t.step("predicated type is correct", () => { 29 | const a: unknown = undefined; 30 | 31 | if (isArrayOf(is.Number)(a)) { 32 | assertType>(true); 33 | } 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /is/array_test.ts: -------------------------------------------------------------------------------- 1 | import { testWithExamples } from "../_testutil.ts"; 2 | import { isArray } from "./array.ts"; 3 | 4 | Deno.test("isArray", async (t) => { 5 | await testWithExamples(t, isArray, { validExamples: ["array"] }); 6 | }); 7 | -------------------------------------------------------------------------------- /is/async_function.ts: -------------------------------------------------------------------------------- 1 | const objectToString = Object.prototype.toString; 2 | 3 | /** 4 | * Return `true` if the type of `x` is `function` (async function). 5 | * 6 | * Use {@linkcode [is/function].isFunction|isFunction} to check if the type of `x` is a function. 7 | * Use {@linkcode [is/sync-function].isSyncFunction|isSyncFunction} to check if the type of `x` is a synchronous function. 8 | * 9 | * ```ts 10 | * import { is } from "@core/unknownutil"; 11 | * 12 | * const a: unknown = async () => {}; 13 | * if (is.AsyncFunction(a)) { 14 | * const _: ((...args: unknown[]) => Promise) = a; 15 | * } 16 | * ``` 17 | */ 18 | export function isAsyncFunction( 19 | x: unknown, 20 | ): x is (...args: unknown[]) => Promise { 21 | return objectToString.call(x) === "[object AsyncFunction]"; 22 | } 23 | -------------------------------------------------------------------------------- /is/async_function_bench.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "@std/assert"; 2 | import { isAsyncFunction as isAsyncFunction420 } from "jsr:@core/unknownutil@4.2.0/is/async-function"; 3 | import { isAsyncFunction } from "./async_function.ts"; 4 | 5 | const repeats = Array.from({ length: 100 }); 6 | const positive: unknown = async () => {}; 7 | const negative: unknown = () => {}; 8 | 9 | Deno.bench({ 10 | name: "current", 11 | fn() { 12 | assert(repeats.every(() => isAsyncFunction(positive))); 13 | }, 14 | group: "isAsyncFunction (positive)", 15 | }); 16 | 17 | Deno.bench({ 18 | name: "current", 19 | fn() { 20 | assert(repeats.every(() => !isAsyncFunction(negative))); 21 | }, 22 | group: "isAsyncFunction (negative)", 23 | }); 24 | 25 | Deno.bench({ 26 | name: "v4.2.0", 27 | fn() { 28 | assert(repeats.every(() => isAsyncFunction420(positive))); 29 | }, 30 | group: "isAsyncFunction (positive)", 31 | }); 32 | 33 | Deno.bench({ 34 | name: "v4.2.0", 35 | fn() { 36 | assert(repeats.every(() => !isAsyncFunction420(negative))); 37 | }, 38 | group: "isAsyncFunction (negative)", 39 | }); 40 | -------------------------------------------------------------------------------- /is/async_function_test.ts: -------------------------------------------------------------------------------- 1 | import { testWithExamples } from "../_testutil.ts"; 2 | import { isAsyncFunction } from "./async_function.ts"; 3 | 4 | Deno.test("isAsyncFunction", async (t) => { 5 | await testWithExamples(t, isAsyncFunction, { 6 | validExamples: ["asyncFunction"], 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /is/bigint.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Return `true` if the type of `x` is `bigint`. 3 | * 4 | * ```ts 5 | * import { is } from "@core/unknownutil"; 6 | * 7 | * const a: unknown = 0n; 8 | * if (is.Bigint(a)) { 9 | * const _: bigint = a; 10 | * } 11 | * ``` 12 | */ 13 | export function isBigint(x: unknown): x is bigint { 14 | return typeof x === "bigint"; 15 | } 16 | -------------------------------------------------------------------------------- /is/bigint_bench.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "@std/assert"; 2 | import { isBigint as isBigint420 } from "jsr:@core/unknownutil@4.2.0/is/bigint"; 3 | import { isBigint } from "./bigint.ts"; 4 | 5 | const repeats = Array.from({ length: 100 }); 6 | const positive: unknown = 0n; 7 | const negative: unknown = 0; 8 | 9 | Deno.bench({ 10 | name: "current", 11 | fn() { 12 | assert(repeats.every(() => isBigint(positive))); 13 | }, 14 | group: "isBigint (positive)", 15 | }); 16 | 17 | Deno.bench({ 18 | name: "current", 19 | fn() { 20 | assert(repeats.every(() => !isBigint(negative))); 21 | }, 22 | group: "isBigint (negative)", 23 | }); 24 | 25 | Deno.bench({ 26 | name: "v4.2.0", 27 | fn() { 28 | assert(repeats.every(() => isBigint420(positive))); 29 | }, 30 | group: "isBigint (positive)", 31 | }); 32 | 33 | Deno.bench({ 34 | name: "v4.2.0", 35 | fn() { 36 | assert(repeats.every(() => !isBigint420(negative))); 37 | }, 38 | group: "isBigint (negative)", 39 | }); 40 | -------------------------------------------------------------------------------- /is/bigint_test.ts: -------------------------------------------------------------------------------- 1 | import { testWithExamples } from "../_testutil.ts"; 2 | import { isBigint } from "./bigint.ts"; 3 | 4 | Deno.test("isBigint", async (t) => { 5 | await testWithExamples(t, isBigint, { validExamples: ["bigint"] }); 6 | }); 7 | -------------------------------------------------------------------------------- /is/boolean.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Return `true` if the type of `x` is `boolean`. 3 | * 4 | * ```ts 5 | * import { is } from "@core/unknownutil"; 6 | * 7 | * const a: unknown = true; 8 | * if (is.Boolean(a)) { 9 | * const _: boolean = a; 10 | * } 11 | * ``` 12 | */ 13 | export function isBoolean(x: unknown): x is boolean { 14 | return typeof x === "boolean"; 15 | } 16 | -------------------------------------------------------------------------------- /is/boolean_bench.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "@std/assert"; 2 | import { isBoolean as isBoolean420 } from "jsr:@core/unknownutil@4.2.0/is/boolean"; 3 | import { isBoolean } from "./boolean.ts"; 4 | 5 | const repeats = Array.from({ length: 100 }); 6 | const positive: unknown = true; 7 | const negative: unknown = 0; 8 | 9 | Deno.bench({ 10 | name: "current", 11 | fn() { 12 | assert(repeats.every(() => isBoolean(positive))); 13 | }, 14 | group: "isBoolean (positive)", 15 | }); 16 | 17 | Deno.bench({ 18 | name: "current", 19 | fn() { 20 | assert(repeats.every(() => !isBoolean(negative))); 21 | }, 22 | group: "isBoolean (negative)", 23 | }); 24 | 25 | Deno.bench({ 26 | name: "v4.2.0", 27 | fn() { 28 | assert(repeats.every(() => isBoolean420(positive))); 29 | }, 30 | group: "isBoolean (positive)", 31 | }); 32 | 33 | Deno.bench({ 34 | name: "v4.2.0", 35 | fn() { 36 | assert(repeats.every(() => !isBoolean420(negative))); 37 | }, 38 | group: "isBoolean (negative)", 39 | }); 40 | -------------------------------------------------------------------------------- /is/boolean_test.ts: -------------------------------------------------------------------------------- 1 | import { testWithExamples } from "../_testutil.ts"; 2 | import { isBoolean } from "./boolean.ts"; 3 | 4 | Deno.test("isBoolean", async (t) => { 5 | await testWithExamples(t, isBoolean, { validExamples: ["boolean"] }); 6 | }); 7 | -------------------------------------------------------------------------------- /is/custom_jsonable.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Represents an object that has a custom `toJSON` method. 3 | * 4 | * Note that `string`, `number`, `boolean`, and `symbol` are not `CustomJsonable` even 5 | * if it's class prototype defines `toJSON` method. 6 | * 7 | * See {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#tojson_behavior|toJSON() behavior} of `JSON.stringify()` for more information. 8 | */ 9 | export type CustomJsonable = { 10 | toJSON(key: string | number): unknown; 11 | }; 12 | 13 | /** 14 | * Returns true if `x` is {@linkcode CustomJsonable}, false otherwise. 15 | * 16 | * Use {@linkcode [is/jsonable].isJsonable|isJsonable} to check if the type of `x` is a JSON-serializable. 17 | * 18 | * ```ts 19 | * import { is, CustomJsonable } from "@core/unknownutil"; 20 | * 21 | * const a: unknown = Object.assign(42n, { 22 | * toJSON() { 23 | * return `${this}n`; 24 | * } 25 | * }); 26 | * if (is.CustomJsonable(a)) { 27 | * const _: CustomJsonable = a; 28 | * } 29 | * ``` 30 | */ 31 | export function isCustomJsonable(x: unknown): x is CustomJsonable { 32 | if (x == null) return false; 33 | switch (typeof x) { 34 | case "bigint": 35 | case "object": 36 | case "function": 37 | return typeof (x as CustomJsonable).toJSON === "function"; 38 | } 39 | return false; 40 | } 41 | -------------------------------------------------------------------------------- /is/custom_jsonable_bench.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "@std/assert"; 2 | import { isCustomJsonable } from "./custom_jsonable.ts"; 3 | 4 | const repeats = Array.from({ length: 100 }); 5 | const positive: unknown = { toJSON: () => "custom" }; 6 | const negative: unknown = {}; 7 | 8 | Deno.bench({ 9 | name: "current", 10 | fn() { 11 | assert(repeats.every(() => isCustomJsonable(positive))); 12 | }, 13 | group: "isCustomJsonable (positive)", 14 | }); 15 | 16 | Deno.bench({ 17 | name: "current", 18 | fn() { 19 | assert(repeats.every(() => !isCustomJsonable(negative))); 20 | }, 21 | group: "isCustomJsonable (negative)", 22 | }); 23 | -------------------------------------------------------------------------------- /is/function.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Return `true` if the type of `x` is `function`. 3 | * 4 | * Use {@linkcode [is/sync-function].isSyncFunction|isSyncFunction} to check if the type of `x` is a synchronous function. 5 | * Use {@linkcode [is/async-function].isAsyncFunction|isAsyncFunction} to check if the type of `x` is an asynchronous function. 6 | * 7 | * ```ts 8 | * import { is } from "@core/unknownutil"; 9 | * 10 | * const a: unknown = () => {}; 11 | * if (is.Function(a)) { 12 | * const _: ((...args: unknown[]) => unknown) = a; 13 | * } 14 | * ``` 15 | */ 16 | export function isFunction(x: unknown): x is (...args: unknown[]) => unknown { 17 | return x instanceof Function; 18 | } 19 | -------------------------------------------------------------------------------- /is/function_bench.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "@std/assert"; 2 | import { isFunction as isFunction420 } from "jsr:@core/unknownutil@4.2.0/is/function"; 3 | import { isFunction } from "./function.ts"; 4 | 5 | const repeats = Array.from({ length: 100 }); 6 | const positive: unknown = () => {}; 7 | const negative: unknown = 0; 8 | 9 | Deno.bench({ 10 | name: "current", 11 | fn() { 12 | assert(repeats.every(() => isFunction(positive))); 13 | }, 14 | group: "isFunction (positive)", 15 | }); 16 | 17 | Deno.bench({ 18 | name: "current", 19 | fn() { 20 | assert(repeats.every(() => !isFunction(negative))); 21 | }, 22 | group: "isFunction (negative)", 23 | }); 24 | 25 | Deno.bench({ 26 | name: "v4.2.0", 27 | fn() { 28 | assert(repeats.every(() => isFunction420(positive))); 29 | }, 30 | group: "isFunction (positive)", 31 | }); 32 | 33 | Deno.bench({ 34 | name: "v4.2.0", 35 | fn() { 36 | assert(repeats.every(() => !isFunction420(negative))); 37 | }, 38 | group: "isFunction (negative)", 39 | }); 40 | -------------------------------------------------------------------------------- /is/function_test.ts: -------------------------------------------------------------------------------- 1 | import { testWithExamples } from "../_testutil.ts"; 2 | import { isFunction } from "./function.ts"; 3 | 4 | Deno.test("isFunction", async (t) => { 5 | await testWithExamples(t, isFunction, { 6 | validExamples: ["syncFunction", "asyncFunction"], 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /is/instance_of.ts: -------------------------------------------------------------------------------- 1 | import { rewriteName } from "../_funcutil.ts"; 2 | import type { Predicate } from "../type.ts"; 3 | 4 | /** 5 | * Return `true` if the type of `x` is instance of `ctor`. 6 | * 7 | * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. 8 | * 9 | * ```ts 10 | * import { is } from "@core/unknownutil"; 11 | * 12 | * const isMyType = is.InstanceOf(Date); 13 | * const a: unknown = new Date(); 14 | * if (isMyType(a)) { 15 | * const _: Date = a; 16 | * } 17 | * ``` 18 | */ 19 | // deno-lint-ignore no-explicit-any 20 | export function isInstanceOf unknown>( 21 | ctor: T, 22 | ): Predicate> { 23 | return rewriteName( 24 | (x: unknown): x is InstanceType => x instanceof ctor, 25 | "isInstanceOf", 26 | ctor, 27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /is/instance_of_bench.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "@std/assert"; 2 | import { isInstanceOf as isInstanceOf420 } from "jsr:@core/unknownutil@4.2.0/is/instance-of"; 3 | import { isInstanceOf } from "./instance_of.ts"; 4 | 5 | const repeats = Array.from({ length: 100 }); 6 | const positive: unknown = Promise.resolve(); 7 | const negative: unknown = ""; 8 | 9 | const pred420 = isInstanceOf420(Promise); 10 | const pred = isInstanceOf(Promise); 11 | 12 | Deno.bench({ 13 | name: "current", 14 | fn() { 15 | assert(repeats.every(() => pred(positive))); 16 | }, 17 | group: "isInstanceOf (positive)", 18 | }); 19 | 20 | Deno.bench({ 21 | name: "current", 22 | fn() { 23 | assert(repeats.every(() => !pred(negative))); 24 | }, 25 | group: "isInstanceOf (negative)", 26 | }); 27 | 28 | Deno.bench({ 29 | name: "v4.2.0", 30 | fn() { 31 | assert(repeats.every(() => pred420(positive))); 32 | }, 33 | group: "isInstanceOf (positive)", 34 | }); 35 | 36 | Deno.bench({ 37 | name: "v4.2.0", 38 | fn() { 39 | assert(repeats.every(() => !pred420(negative))); 40 | }, 41 | group: "isInstanceOf (negative)", 42 | }); 43 | -------------------------------------------------------------------------------- /is/instance_of_test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from "@std/assert"; 2 | import { assertType, type IsExact } from "@std/testing/types"; 3 | import { isInstanceOf } from "./instance_of.ts"; 4 | 5 | Deno.test("isInstanceOf", async (t) => { 6 | await t.step("returns properly named predicate function", () => { 7 | assertEquals(typeof isInstanceOf(Date), "function"); 8 | assertEquals(isInstanceOf(Date).name, "isInstanceOf(Date)"); 9 | assertEquals(isInstanceOf(class {}).name, "isInstanceOf((anonymous))"); 10 | }); 11 | 12 | await t.step("returns true on T instance", () => { 13 | class Cls {} 14 | assertEquals(isInstanceOf(Cls)(new Cls()), true); 15 | assertEquals(isInstanceOf(Date)(new Date()), true); 16 | assertEquals(isInstanceOf(Promise)(new Promise(() => {})), true); 17 | }); 18 | 19 | await t.step("returns false on non T instance", () => { 20 | class Cls {} 21 | assertEquals(isInstanceOf(Date)(new Cls()), false); 22 | assertEquals(isInstanceOf(Promise)(new Date()), false); 23 | assertEquals(isInstanceOf(Cls)(new Promise(() => {})), false); 24 | }); 25 | 26 | await t.step("predicated type is correct", () => { 27 | class Cls {} 28 | const a: unknown = undefined; 29 | 30 | if (isInstanceOf(Cls)(a)) { 31 | assertType>(true); 32 | } 33 | 34 | if (isInstanceOf(Date)(a)) { 35 | assertType>(true); 36 | } 37 | 38 | if (isInstanceOf(Promise)(a)) { 39 | assertType>>(true); 40 | } 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /is/intersection_of.ts: -------------------------------------------------------------------------------- 1 | import { rewriteName } from "../_funcutil.ts"; 2 | import { hasAnnotation, type IsPredObj } from "../_annotation.ts"; 3 | import type { Predicate } from "../type.ts"; 4 | import { isObjectOf } from "./object_of.ts"; 5 | 6 | /** 7 | * Return a type predicate function that returns `true` if the type of `x` is `IntersectionOf`. 8 | * 9 | * Use {@linkcode [is/union-of].isUnionOf|isUnionOf} to check if the type of `x` is a union of `T`. 10 | * 11 | * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. 12 | * 13 | * ```ts 14 | * import { is } from "@core/unknownutil"; 15 | * 16 | * const isMyType = is.IntersectionOf([ 17 | * is.ObjectOf({ a: is.Number }), 18 | * is.ObjectOf({ b: is.String }), 19 | * ]); 20 | * const a: unknown = { a: 0, b: "a" }; 21 | * if (isMyType(a)) { 22 | * const _: { a: number } & { b: string } = a; 23 | * } 24 | * ``` 25 | * 26 | * Depending on the version of TypeScript and how values are provided, it may be necessary to add `as const` to the array 27 | * used as `preds`. If a type error occurs, try adding `as const` as follows: 28 | * 29 | * ```ts 30 | * import { is } from "@core/unknownutil"; 31 | * 32 | * const preds = [ 33 | * is.ObjectOf({ a: is.Number }), 34 | * is.ObjectOf({ b: is.String }), 35 | * ] as const 36 | * const isMyType = is.IntersectionOf(preds); 37 | * const a: unknown = { a: 0, b: "a" }; 38 | * if (isMyType(a)) { 39 | * const _: { a: number } & { b: string } = a; 40 | * } 41 | * ``` 42 | */ 43 | export function isIntersectionOf< 44 | T extends readonly [ 45 | Predicate & IsPredObj, 46 | ...(Predicate & IsPredObj)[], 47 | ], 48 | >( 49 | preds: T, 50 | ): 51 | & Predicate> 52 | & IsPredObj; 53 | export function isIntersectionOf< 54 | T extends readonly [Predicate], 55 | >( 56 | preds: T, 57 | ): T[0]; 58 | export function isIntersectionOf< 59 | T extends readonly [Predicate, ...Predicate[]], 60 | >( 61 | preds: T, 62 | ): 63 | & Predicate> 64 | & IsPredObj; 65 | export function isIntersectionOf< 66 | T extends readonly [Predicate, ...Predicate[]], 67 | >( 68 | preds: T, 69 | ): 70 | | Predicate 71 | | Predicate> 72 | & IsPredObj { 73 | const predObj = {}; 74 | const restPreds = preds.filter((pred) => { 75 | if (!hasAnnotation(pred, "predObj")) { 76 | return true; 77 | } 78 | Object.assign(predObj, pred.predObj); 79 | }); 80 | if (restPreds.length < preds.length) { 81 | restPreds.push(isObjectOf(predObj)); 82 | } 83 | if (restPreds.length === 1) { 84 | return restPreds[0]; 85 | } 86 | return rewriteName( 87 | (x: unknown): x is IntersectionOf => restPreds.every((pred) => pred(x)), 88 | "isIntersectionOf", 89 | preds, 90 | ); 91 | } 92 | 93 | type TupleToIntersection = T extends readonly [] ? never 94 | : T extends readonly [infer U] ? U 95 | : T extends readonly [infer U, ...infer R] ? U & TupleToIntersection 96 | : never; 97 | 98 | type IntersectionOf = TupleToIntersection< 99 | { 100 | -readonly [P in keyof T]: T[P] extends Predicate ? U : never; 101 | } 102 | >; 103 | -------------------------------------------------------------------------------- /is/intersection_of_bench.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "@std/assert"; 2 | import { isObjectOf } from "./object_of.ts"; 3 | import { isIntersectionOf as isIntersectionOf420 } from "jsr:@core/unknownutil@4.2.0/is/intersection-of"; 4 | import { isIntersectionOf } from "./intersection_of.ts"; 5 | 6 | const repeats = Array.from({ length: 100 }); 7 | const positive: unknown = { a: "", b: 0, c: true }; 8 | const positive10: unknown = Object.assign( 9 | { a: "", b: 0, c: true }, 10 | Array.from({ length: 10 }), 11 | ); 12 | const positive1000: unknown = Object.assign( 13 | { a: "", b: 0, c: true }, 14 | Array.from({ length: 1000 }), 15 | ); 16 | const negative: unknown = {}; 17 | const negative10: unknown = Object.assign({}, Array.from({ length: 10 })); 18 | const negative1000: unknown = Object.assign({}, Array.from({ length: 1000 })); 19 | 20 | const preds = [ 21 | isObjectOf({ 22 | a: (x: unknown): x is string => typeof x === "string", 23 | }), 24 | isObjectOf({ 25 | b: (x: unknown): x is number => typeof x === "number", 26 | }), 27 | isObjectOf({ 28 | c: (x: unknown): x is boolean => typeof x === "boolean", 29 | }), 30 | // deno-lint-ignore ban-types 31 | (x: unknown): x is {} => typeof x === "object", 32 | ] as const; 33 | const pred420 = isIntersectionOf420(preds); 34 | const pred = isIntersectionOf(preds); 35 | 36 | Deno.bench({ 37 | name: "current", 38 | fn() { 39 | assert(repeats.every(() => pred(positive))); 40 | }, 41 | group: "isIntersectionOf (positive)", 42 | }); 43 | 44 | Deno.bench({ 45 | name: "current", 46 | fn() { 47 | assert(repeats.every(() => pred(positive10))); 48 | }, 49 | group: "isIntersectionOf (positive 10)", 50 | }); 51 | 52 | Deno.bench({ 53 | name: "current", 54 | fn() { 55 | assert(repeats.every(() => pred(positive1000))); 56 | }, 57 | group: "isIntersectionOf (positive 1000)", 58 | }); 59 | 60 | Deno.bench({ 61 | name: "current", 62 | fn() { 63 | assert(repeats.every(() => !pred(negative))); 64 | }, 65 | group: "isIntersectionOf (negative)", 66 | }); 67 | 68 | Deno.bench({ 69 | name: "current", 70 | fn() { 71 | assert(repeats.every(() => !pred(negative10))); 72 | }, 73 | group: "isIntersectionOf (negative 10)", 74 | }); 75 | 76 | Deno.bench({ 77 | name: "current", 78 | fn() { 79 | assert(repeats.every(() => !pred(negative1000))); 80 | }, 81 | group: "isIntersectionOf (negative 1000)", 82 | }); 83 | 84 | Deno.bench({ 85 | name: "v4.2.0", 86 | fn() { 87 | assert(repeats.every(() => pred420(positive))); 88 | }, 89 | group: "isIntersectionOf (positive)", 90 | }); 91 | 92 | Deno.bench({ 93 | name: "v4.2.0", 94 | fn() { 95 | assert(repeats.every(() => pred420(positive10))); 96 | }, 97 | group: "isIntersectionOf (positive 10)", 98 | }); 99 | 100 | Deno.bench({ 101 | name: "v4.2.0", 102 | fn() { 103 | assert(repeats.every(() => pred420(positive1000))); 104 | }, 105 | group: "isIntersectionOf (positive 1000)", 106 | }); 107 | 108 | Deno.bench({ 109 | name: "v4.2.0", 110 | fn() { 111 | assert(repeats.every(() => !pred420(negative))); 112 | }, 113 | group: "isIntersectionOf (negative)", 114 | }); 115 | 116 | Deno.bench({ 117 | name: "v4.2.0", 118 | fn() { 119 | assert(repeats.every(() => !pred420(negative10))); 120 | }, 121 | group: "isIntersectionOf (negative 10)", 122 | }); 123 | 124 | Deno.bench({ 125 | name: "v4.2.0", 126 | fn() { 127 | assert(repeats.every(() => !pred420(negative1000))); 128 | }, 129 | group: "isIntersectionOf (negative 1000)", 130 | }); 131 | -------------------------------------------------------------------------------- /is/jsonable.ts: -------------------------------------------------------------------------------- 1 | import { type CustomJsonable, isCustomJsonable } from "./custom_jsonable.ts"; 2 | 3 | /** 4 | * Represents a JSON-serializable value. 5 | * 6 | * See {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#description|Description} of `JSON.stringify()` for more information. 7 | */ 8 | export type Jsonable = 9 | | string 10 | | number 11 | | boolean 12 | | null 13 | | unknown[] 14 | | { [key: string]: unknown } 15 | | CustomJsonable; 16 | 17 | /** 18 | * Returns true if `x` is a JSON-serializable value, false otherwise. 19 | * 20 | * It does not check array or object properties recursively. 21 | * 22 | * Use {@linkcode [is/custom_jsonable].isCustomJsonable|isCustomJsonable} to check if the type of `x` has a custom `toJSON` method. 23 | * 24 | * ```ts 25 | * import { is, Jsonable } from "@core/unknownutil"; 26 | * 27 | * const a: unknown = "Hello, world!"; 28 | * if (is.Jsonable(a)) { 29 | * const _: Jsonable = a; 30 | * } 31 | * ``` 32 | */ 33 | export function isJsonable(x: unknown): x is Jsonable { 34 | switch (typeof x) { 35 | case "undefined": 36 | return false; 37 | case "string": 38 | case "number": 39 | case "boolean": 40 | return true; 41 | case "bigint": 42 | case "symbol": 43 | case "function": 44 | return isCustomJsonable(x); 45 | case "object": { 46 | if (x === null || Array.isArray(x)) return true; 47 | const p = Object.getPrototypeOf(x); 48 | if (p === BigInt.prototype || p === Function.prototype) { 49 | return isCustomJsonable(x); 50 | } 51 | return true; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /is/jsonable_bench.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "@std/assert"; 2 | import { isJsonable } from "./jsonable.ts"; 3 | import { buildTestcases } from "./custom_jsonable_test.ts"; 4 | 5 | const repeats = Array.from({ length: 100 }); 6 | 7 | for (const [name, value] of buildTestcases()) { 8 | switch (name) { 9 | case "undefined": 10 | case "bigint": 11 | case "function": 12 | case "symbol": 13 | Deno.bench({ 14 | name: "current", 15 | fn() { 16 | assert(repeats.every(() => !isJsonable(value))); 17 | }, 18 | group: `isJsonable (${name})`, 19 | }); 20 | break; 21 | default: 22 | Deno.bench({ 23 | name: "current", 24 | fn() { 25 | assert(repeats.every(() => isJsonable(value))); 26 | }, 27 | group: `isJsonable (${name})`, 28 | }); 29 | } 30 | } 31 | 32 | for (const [name, value] of buildTestcases()) { 33 | switch (name) { 34 | case "undefined": 35 | case "null": 36 | continue; 37 | case "bigint": 38 | case "function": 39 | Deno.bench({ 40 | name: "current", 41 | fn() { 42 | const v = Object.assign(value as NonNullable, { 43 | toJSON: () => "custom", 44 | }); 45 | assert(repeats.every(() => isJsonable(v))); 46 | }, 47 | group: `isJsonable (${name} with own toJSON method)`, 48 | }); 49 | } 50 | } 51 | 52 | for (const [name, value] of buildTestcases()) { 53 | switch (name) { 54 | case "bigint": 55 | case "function": 56 | Deno.bench({ 57 | name: "current", 58 | fn() { 59 | const proto = Object.getPrototypeOf(value); 60 | proto.toJSON = () => "custom"; 61 | try { 62 | assert(repeats.every(() => isJsonable(value))); 63 | } finally { 64 | delete proto.toJSON; 65 | } 66 | }, 67 | group: 68 | `isJsonable (${name} with class prototype defines toJSON method)`, 69 | }); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /is/literal_of.ts: -------------------------------------------------------------------------------- 1 | import { rewriteName } from "../_funcutil.ts"; 2 | import type { Predicate, Primitive } from "../type.ts"; 3 | 4 | /** 5 | * Return a type predicate function that returns `true` if the type of `x` is a literal type of `pred`. 6 | * 7 | * Use {@linkcode [is/literal].isLiteral|isLiteral} to check if the type of `x` is a literal type. 8 | * Use {@linkcode [is/literal-one-of].isLiteralOneOf|isLiteralOneOf} to check if the type of `x` is one of the literal type of {@linkcode [type].Primitive|Primitive[]}. 9 | * 10 | * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. 11 | * 12 | * ```ts 13 | * import { is } from "@core/unknownutil"; 14 | * 15 | * const isMyType = is.LiteralOf("hello"); 16 | * const a: unknown = "hello"; 17 | * if (isMyType(a)) { 18 | * const _: "hello" = a; 19 | * } 20 | * ``` 21 | */ 22 | export function isLiteralOf( 23 | literal: T, 24 | ): Predicate { 25 | return rewriteName( 26 | (x: unknown): x is T => x === literal, 27 | "isLiteralOf", 28 | literal, 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /is/literal_of_bench.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "@std/assert"; 2 | import { isLiteralOf as isLiteralOf420 } from "jsr:@core/unknownutil@4.2.0/is/literal-of"; 3 | import { isLiteralOf } from "./literal_of.ts"; 4 | 5 | const repeats = Array.from({ length: 100 }); 6 | const positive: unknown = "hello"; 7 | const negative: unknown = "world"; 8 | 9 | const pred420 = isLiteralOf420("hello"); 10 | const pred = isLiteralOf("hello"); 11 | 12 | Deno.bench({ 13 | name: "current", 14 | fn() { 15 | assert(repeats.every(() => pred(positive))); 16 | }, 17 | group: "isLiteralOf (positive)", 18 | }); 19 | 20 | Deno.bench({ 21 | name: "current", 22 | fn() { 23 | assert(repeats.every(() => !pred(negative))); 24 | }, 25 | group: "isLiteralOf (negative)", 26 | }); 27 | 28 | Deno.bench({ 29 | name: "v4.2.0", 30 | fn() { 31 | assert(repeats.every(() => pred420(positive))); 32 | }, 33 | group: "isLiteralOf (positive)", 34 | }); 35 | 36 | Deno.bench({ 37 | name: "v4.2.0", 38 | fn() { 39 | assert(repeats.every(() => !pred420(negative))); 40 | }, 41 | group: "isLiteralOf (negative)", 42 | }); 43 | -------------------------------------------------------------------------------- /is/literal_of_test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from "@std/assert"; 2 | import { assertType, type IsExact } from "@std/testing/types"; 3 | import { isLiteralOf } from "./literal_of.ts"; 4 | 5 | Deno.test("isLiteralOf", async (t) => { 6 | await t.step("returns properly named predicate function", () => { 7 | assertEquals(typeof isLiteralOf("hello"), "function"); 8 | assertEquals(isLiteralOf("hello").name, `isLiteralOf("hello")`); 9 | assertEquals(isLiteralOf(100).name, `isLiteralOf(100)`); 10 | assertEquals(isLiteralOf(100n).name, `isLiteralOf(100n)`); 11 | assertEquals(isLiteralOf(true).name, `isLiteralOf(true)`); 12 | assertEquals(isLiteralOf(false).name, `isLiteralOf(false)`); 13 | assertEquals(isLiteralOf(null).name, `isLiteralOf(null)`); 14 | assertEquals(isLiteralOf(undefined).name, `isLiteralOf(undefined)`); 15 | assertEquals(isLiteralOf(Symbol("asdf")).name, `isLiteralOf(Symbol(asdf))`); 16 | }); 17 | 18 | await t.step("returns true on literal T", () => { 19 | const s = Symbol("asdf"); 20 | assertEquals(isLiteralOf("hello")("hello"), true); 21 | assertEquals(isLiteralOf(100)(100), true); 22 | assertEquals(isLiteralOf(100n)(100n), true); 23 | assertEquals(isLiteralOf(true)(true), true); 24 | assertEquals(isLiteralOf(false)(false), true); 25 | assertEquals(isLiteralOf(null)(null), true); 26 | assertEquals(isLiteralOf(undefined)(undefined), true); 27 | assertEquals(isLiteralOf(s)(s), true); 28 | }); 29 | 30 | await t.step("returns false on non literal T", () => { 31 | const s = Symbol("asdf"); 32 | assertEquals(isLiteralOf(100)("hello"), false); 33 | assertEquals(isLiteralOf(100n)(100), false); 34 | assertEquals(isLiteralOf(true)(100n), false); 35 | assertEquals(isLiteralOf(false)(true), false); 36 | assertEquals(isLiteralOf(null)(false), false); 37 | assertEquals(isLiteralOf(undefined)(null), false); 38 | assertEquals(isLiteralOf(s)(undefined), false); 39 | assertEquals(isLiteralOf("hello")(s), false); 40 | }); 41 | 42 | await t.step("predicated type is correct", () => { 43 | const s = Symbol("asdf"); 44 | const a: unknown = undefined; 45 | 46 | if (isLiteralOf("hello")(a)) { 47 | assertType>(true); 48 | } 49 | 50 | if (isLiteralOf(100)(a)) { 51 | assertType>(true); 52 | } 53 | 54 | if (isLiteralOf(100n)(a)) { 55 | assertType>(true); 56 | } 57 | 58 | if (isLiteralOf(true)(a)) { 59 | assertType>(true); 60 | } 61 | 62 | if (isLiteralOf(false)(a)) { 63 | assertType>(true); 64 | } 65 | 66 | if (isLiteralOf(null)(a)) { 67 | assertType>(true); 68 | } 69 | 70 | if (isLiteralOf(undefined)(a)) { 71 | assertType>(true); 72 | } 73 | 74 | if (isLiteralOf(s)(a)) { 75 | assertType>(true); 76 | } 77 | }); 78 | }); 79 | -------------------------------------------------------------------------------- /is/literal_one_of.ts: -------------------------------------------------------------------------------- 1 | import { rewriteName } from "../_funcutil.ts"; 2 | import type { Predicate, Primitive } from "../type.ts"; 3 | 4 | /** 5 | * Return a type predicate function that returns `true` if the type of `x` is one of literal type in `preds`. 6 | * 7 | * Use {@linkcode [is/literal].isLiteral|isLiteral} to check if the type of `x` is a literal type. 8 | * Use {@linkcode [is/literal-of].isLiteralOf|isLiteralOf} to check if the type of `x` is a literal type of {@linkcode [type].Primitive|Primitive}. 9 | * 10 | * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. 11 | * 12 | * ```ts 13 | * import { is } from "@core/unknownutil"; 14 | * 15 | * const isMyType = is.LiteralOneOf(["hello", "world"] as const); 16 | * const a: unknown = "hello"; 17 | * if (isMyType(a)) { 18 | * const _: "hello" | "world" = a; 19 | * } 20 | * ``` 21 | */ 22 | export function isLiteralOneOf( 23 | literals: T, 24 | ): Predicate { 25 | const s = new Set(literals); 26 | return rewriteName( 27 | (x: unknown): x is T[number] => s.has(x as T[number]), 28 | "isLiteralOneOf", 29 | literals, 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /is/literal_one_of_bench.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "@std/assert"; 2 | import { isLiteralOneOf as isLiteralOneOf420 } from "jsr:@core/unknownutil@4.2.0/is/literal-one-of"; 3 | import { isLiteralOneOf } from "./literal_one_of.ts"; 4 | 5 | const repeats = Array.from({ length: 100 }); 6 | const positive: unknown = "hello"; 7 | const negative: unknown = "goodbye"; 8 | 9 | const pred420 = isLiteralOneOf420(["hello", "world"] as const); 10 | const pred = isLiteralOneOf(["hello", "world"] as const); 11 | 12 | Deno.bench({ 13 | name: "current", 14 | fn() { 15 | assert(repeats.every(() => pred(positive))); 16 | }, 17 | group: "isLiteralOneOf (positive)", 18 | }); 19 | 20 | Deno.bench({ 21 | name: "current", 22 | fn() { 23 | assert(repeats.every(() => !pred(negative))); 24 | }, 25 | group: "isLiteralOneOf (negative)", 26 | }); 27 | 28 | Deno.bench({ 29 | name: "v4.2.0", 30 | fn() { 31 | assert(repeats.every(() => pred420(positive))); 32 | }, 33 | group: "isLiteralOneOf (positive)", 34 | }); 35 | 36 | Deno.bench({ 37 | name: "v4.2.0", 38 | fn() { 39 | assert(repeats.every(() => !pred420(negative))); 40 | }, 41 | group: "isLiteralOneOf (negative)", 42 | }); 43 | -------------------------------------------------------------------------------- /is/literal_one_of_test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from "@std/assert"; 2 | import { assertType, type IsExact } from "@std/testing/types"; 3 | import { isLiteralOneOf } from "./literal_one_of.ts"; 4 | 5 | Deno.test("isLiteralOneOf", async (t) => { 6 | const literals = ["hello", "world"] as const; 7 | await t.step("returns properly named predicate function", () => { 8 | assertEquals(typeof isLiteralOneOf(literals), "function"); 9 | assertEquals( 10 | isLiteralOneOf(literals).name, 11 | `isLiteralOneOf(["hello", "world"])`, 12 | ); 13 | }); 14 | 15 | await t.step("returns true on literal T", () => { 16 | assertEquals(isLiteralOneOf(literals)("hello"), true); 17 | assertEquals(isLiteralOneOf(literals)("world"), true); 18 | }); 19 | 20 | await t.step("returns false on non literal T", () => { 21 | assertEquals(isLiteralOneOf(literals)(""), false); 22 | assertEquals(isLiteralOneOf(literals)(100), false); 23 | }); 24 | 25 | await t.step("returns proper type predicate", () => { 26 | const a: unknown = "hello"; 27 | if (isLiteralOneOf(literals)(a)) { 28 | assertType>(true); 29 | } 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /is/map.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Return `true` if the type of `x` is `Map`. 3 | * 4 | * Use {@linkcode [is/map-of].isMapOf|isMapOf} to check if the type of `x` is a map of `T`. 5 | * 6 | * ```ts 7 | * import { is } from "@core/unknownutil"; 8 | * 9 | * const a: unknown = new Map([["a", 0], ["b", 1]]); 10 | * if (is.Map(a)) { 11 | * const _: Map = a; 12 | * } 13 | * ``` 14 | */ 15 | export function isMap(x: unknown): x is Map { 16 | return x instanceof Map; 17 | } 18 | -------------------------------------------------------------------------------- /is/map_bench.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "@std/assert"; 2 | import { isMap as isMap420 } from "jsr:@core/unknownutil@4.2.0/is/map"; 3 | import { isMap } from "./map.ts"; 4 | 5 | const repeats = Array.from({ length: 100 }); 6 | const positive: unknown = new Map(); 7 | const negative: unknown = {}; 8 | 9 | Deno.bench({ 10 | name: "current", 11 | fn() { 12 | assert(repeats.every(() => isMap(positive))); 13 | }, 14 | group: "isMap (positive)", 15 | }); 16 | 17 | Deno.bench({ 18 | name: "current", 19 | fn() { 20 | assert(repeats.every(() => !isMap(negative))); 21 | }, 22 | group: "isMap (negative)", 23 | }); 24 | 25 | Deno.bench({ 26 | name: "v4.2.0", 27 | fn() { 28 | assert(repeats.every(() => isMap420(positive))); 29 | }, 30 | group: "isMap (positive)", 31 | }); 32 | 33 | Deno.bench({ 34 | name: "v4.2.0", 35 | fn() { 36 | assert(repeats.every(() => !isMap420(negative))); 37 | }, 38 | group: "isMap (negative)", 39 | }); 40 | -------------------------------------------------------------------------------- /is/map_of.ts: -------------------------------------------------------------------------------- 1 | import { rewriteName } from "../_funcutil.ts"; 2 | import type { Predicate } from "../type.ts"; 3 | import { isMap } from "./map.ts"; 4 | 5 | /** 6 | * Return a type predicate function that returns `true` if the type of `x` is `Map`. 7 | * 8 | * Use {@linkcode [is/map].isMap|isMap} to check if the type of `x` is a map of `unknown`. 9 | * 10 | * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. 11 | * 12 | * ```ts 13 | * import { is } from "@core/unknownutil"; 14 | * 15 | * const isMyType = is.MapOf(is.Number); 16 | * const a: unknown = new Map([["a", 0], ["b", 1]]); 17 | * if (isMyType(a)) { 18 | * const _: Map = a; 19 | * } 20 | * ``` 21 | * 22 | * With predicate function for keys: 23 | * 24 | * ```ts 25 | * import { is } from "@core/unknownutil"; 26 | * 27 | * const isMyType = is.MapOf(is.Number, is.String); 28 | * const a: unknown = new Map([["a", 0], ["b", 1]]); 29 | * if (isMyType(a)) { 30 | * const _: Map = a; 31 | * } 32 | * ``` 33 | */ 34 | export function isMapOf( 35 | pred: Predicate, 36 | predKey?: Predicate, 37 | ): Predicate> { 38 | return rewriteName( 39 | (x: unknown): x is Map => { 40 | if (!isMap(x)) return false; 41 | for (const [k, v] of x.entries()) { 42 | if (!pred(v)) return false; 43 | if (predKey && !predKey(k)) return false; 44 | } 45 | return true; 46 | }, 47 | "isMapOf", 48 | pred, 49 | predKey, 50 | ); 51 | } 52 | -------------------------------------------------------------------------------- /is/map_of_test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from "@std/assert"; 2 | import { assertType, type IsExact } from "@std/testing/types"; 3 | import { is } from "./mod.ts"; 4 | import { isMapOf } from "./map_of.ts"; 5 | 6 | Deno.test("isMapOf", async (t) => { 7 | await t.step("returns properly named predicate function", () => { 8 | assertEquals(typeof isMapOf(is.Number), "function"); 9 | assertEquals(isMapOf(is.Number).name, "isMapOf(isNumber, undefined)"); 10 | assertEquals( 11 | isMapOf((_x): _x is unknown => true).name, 12 | "isMapOf((anonymous), undefined)", 13 | ); 14 | }); 15 | 16 | await t.step("returns true on T map", () => { 17 | assertEquals(isMapOf(is.Number)(new Map([["a", 0]])), true); 18 | assertEquals(isMapOf(is.String)(new Map([["a", "a"]])), true); 19 | assertEquals(isMapOf(is.Boolean)(new Map([["a", true]])), true); 20 | }); 21 | 22 | await t.step("returns false on non T map", () => { 23 | assertEquals(isMapOf(is.String)(new Map([["a", 0]])), false); 24 | assertEquals(isMapOf(is.Number)(new Map([["a", "a"]])), false); 25 | assertEquals(isMapOf(is.String)(new Map([["a", true]])), false); 26 | }); 27 | 28 | await t.step("returns proper type predicate", () => { 29 | const a: unknown = undefined; 30 | if (isMapOf(is.Number)(a)) { 31 | assertType>>(true); 32 | } 33 | }); 34 | }); 35 | 36 | Deno.test("isMapOf", async (t) => { 37 | await t.step("returns properly named predicate function", () => { 38 | assertEquals(typeof isMapOf(is.Number, is.String), "function"); 39 | assertEquals( 40 | isMapOf(is.Number, is.String).name, 41 | "isMapOf(isNumber, isString)", 42 | ); 43 | assertEquals( 44 | isMapOf((_x): _x is unknown => true, is.String).name, 45 | "isMapOf((anonymous), isString)", 46 | ); 47 | }); 48 | 49 | await t.step("returns true on T map", () => { 50 | assertEquals(isMapOf(is.Number, is.String)(new Map([["a", 0]])), true); 51 | assertEquals(isMapOf(is.String, is.String)(new Map([["a", "a"]])), true); 52 | assertEquals(isMapOf(is.Boolean, is.String)(new Map([["a", true]])), true); 53 | }); 54 | 55 | await t.step("returns false on non T map", () => { 56 | assertEquals(isMapOf(is.String, is.String)(new Map([["a", 0]])), false); 57 | assertEquals(isMapOf(is.Number, is.String)(new Map([["a", "a"]])), false); 58 | assertEquals(isMapOf(is.String, is.String)(new Map([["a", true]])), false); 59 | }); 60 | 61 | await t.step("returns false on non K map", () => { 62 | assertEquals(isMapOf(is.Number, is.Number)(new Map([["a", 0]])), false); 63 | assertEquals(isMapOf(is.String, is.Number)(new Map([["a", "a"]])), false); 64 | assertEquals(isMapOf(is.Boolean, is.Number)(new Map([["a", true]])), false); 65 | }); 66 | 67 | await t.step("predicated type is correct", () => { 68 | const a: unknown = new Map([["a", 0]]); 69 | if (isMapOf(is.Number, is.String)(a)) { 70 | assertType>>(true); 71 | } 72 | }); 73 | }); 74 | -------------------------------------------------------------------------------- /is/map_test.ts: -------------------------------------------------------------------------------- 1 | import { testWithExamples } from "../_testutil.ts"; 2 | import { isMap } from "./map.ts"; 3 | 4 | Deno.test("isMap", async (t) => { 5 | await testWithExamples(t, isMap, { 6 | validExamples: ["map"], 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /is/mod_test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from "@std/assert"; 2 | import { globToRegExp } from "@std/path"; 3 | import { is } from "./mod.ts"; 4 | 5 | const excludes = [ 6 | "mod.ts", 7 | "_*.ts", 8 | "*_bench.ts", 9 | "*_test.ts", 10 | ]; 11 | 12 | Deno.test("is", async (t) => { 13 | const names = await listIsFunctions(); 14 | await t.step( 15 | "must have all `is*` function aliases as entries", 16 | () => { 17 | assertEquals(Object.keys(is).sort(), names); 18 | }, 19 | ); 20 | }); 21 | 22 | async function listIsFunctions(): Promise { 23 | const patterns = excludes.map((p) => globToRegExp(p)); 24 | const names: string[] = []; 25 | for await (const entry of Deno.readDir(import.meta.dirname!)) { 26 | if (!entry.isFile || !entry.name.endsWith(".ts")) continue; 27 | if (patterns.some((p) => p.test(entry.name))) continue; 28 | const mod = await import(import.meta.resolve(`./${entry.name}`)); 29 | const isFunctionNames = Object.entries(mod) 30 | .filter(([k, _]) => k.startsWith("is")) 31 | .map(([k, _]) => k.slice(2)); 32 | names.push(...isFunctionNames); 33 | } 34 | return names.toSorted(); 35 | } 36 | -------------------------------------------------------------------------------- /is/null.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Return `true` if the type of `x` is `null`. 3 | * 4 | * Use {@linkcode [is/undefined].isUndefined|isUndefined} to check if the type of `x` is `undefined`. 5 | * Use {@linkcode [is/nullish].isNullish|isNullish} to check if the type of `x` is `null` or `undefined`. 6 | * 7 | * ```ts 8 | * import { is } from "@core/unknownutil"; 9 | * 10 | * const a: unknown = null; 11 | * if (is.Null(a)) { 12 | * const _: null = a; 13 | * } 14 | * ``` 15 | */ 16 | export function isNull(x: unknown): x is null { 17 | return x === null; 18 | } 19 | -------------------------------------------------------------------------------- /is/null_bench.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "@std/assert"; 2 | import { isNull as isNull420 } from "jsr:@core/unknownutil@4.2.0/is/null"; 3 | import { isNull } from "./null.ts"; 4 | 5 | const repeats = Array.from({ length: 100 }); 6 | const positive: unknown = null; 7 | const negative: unknown = undefined; 8 | 9 | Deno.bench({ 10 | name: "current", 11 | fn() { 12 | assert(repeats.every(() => isNull(positive))); 13 | }, 14 | group: "isNull (positive)", 15 | }); 16 | 17 | Deno.bench({ 18 | name: "current", 19 | fn() { 20 | assert(repeats.every(() => !isNull(negative))); 21 | }, 22 | group: "isNull (negative)", 23 | }); 24 | 25 | Deno.bench({ 26 | name: "v4.2.0", 27 | fn() { 28 | assert(repeats.every(() => isNull420(positive))); 29 | }, 30 | group: "isNull (positive)", 31 | }); 32 | 33 | Deno.bench({ 34 | name: "v4.2.0", 35 | fn() { 36 | assert(repeats.every(() => !isNull420(negative))); 37 | }, 38 | group: "isNull (negative)", 39 | }); 40 | -------------------------------------------------------------------------------- /is/null_test.ts: -------------------------------------------------------------------------------- 1 | import { testWithExamples } from "../_testutil.ts"; 2 | import { isNull } from "./null.ts"; 3 | 4 | Deno.test("isNull", async (t) => { 5 | await testWithExamples(t, isNull, { validExamples: ["null"] }); 6 | }); 7 | -------------------------------------------------------------------------------- /is/nullish.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Return `true` if the type of `x` is `null` or `undefined`. 3 | * 4 | * Use {@linkcode [is/null].isNull|isNull} to check if the type of `x` is `null`. 5 | * Use {@linkcode [is/undefined].isUndefined|isUndefined} to check if the type of `x` is `undefined`. 6 | * 7 | * ```ts 8 | * import { is } from "@core/unknownutil"; 9 | * 10 | * const a: unknown = null; 11 | * if (is.Nullish(a)) { 12 | * const _: (null | undefined) = a; 13 | * } 14 | * ``` 15 | */ 16 | export function isNullish(x: unknown): x is null | undefined { 17 | return x == null; 18 | } 19 | -------------------------------------------------------------------------------- /is/nullish_bench.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "@std/assert"; 2 | import { isNullish as isNullish420 } from "jsr:@core/unknownutil@4.2.0/is/nullish"; 3 | import { isNullish } from "./nullish.ts"; 4 | 5 | const repeats = Array.from({ length: 100 }); 6 | const positive: unknown = null; 7 | const negative: unknown = 0; 8 | 9 | Deno.bench({ 10 | name: "current", 11 | fn() { 12 | assert(repeats.every(() => isNullish(positive))); 13 | }, 14 | group: "isNullish (positive)", 15 | }); 16 | 17 | Deno.bench({ 18 | name: "current", 19 | fn() { 20 | assert(repeats.every(() => !isNullish(negative))); 21 | }, 22 | group: "isNullish (negative)", 23 | }); 24 | 25 | Deno.bench({ 26 | name: "v4.2.0", 27 | fn() { 28 | assert(repeats.every(() => isNullish420(positive))); 29 | }, 30 | group: "isNullish (positive)", 31 | }); 32 | 33 | Deno.bench({ 34 | name: "v4.2.0", 35 | fn() { 36 | assert(repeats.every(() => !isNullish420(negative))); 37 | }, 38 | group: "isNullish (negative)", 39 | }); 40 | -------------------------------------------------------------------------------- /is/nullish_test.ts: -------------------------------------------------------------------------------- 1 | import { testWithExamples } from "../_testutil.ts"; 2 | import { isNullish } from "./nullish.ts"; 3 | 4 | Deno.test("isNullish", async (t) => { 5 | await testWithExamples(t, isNullish, { 6 | validExamples: ["null", "undefined"], 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /is/number.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Return `true` if the type of `x` is `number`. 3 | * 4 | * ```ts 5 | * import { is } from "@core/unknownutil"; 6 | * 7 | * const a: unknown = 0; 8 | * if (is.Number(a)) { 9 | * const _: number = a; 10 | * } 11 | * ``` 12 | */ 13 | export function isNumber(x: unknown): x is number { 14 | return typeof x === "number"; 15 | } 16 | -------------------------------------------------------------------------------- /is/number_bench.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "@std/assert"; 2 | import { isNumber as isNumber420 } from "jsr:@core/unknownutil@4.2.0/is/number"; 3 | import { isNumber } from "./number.ts"; 4 | 5 | const repeats = Array.from({ length: 100 }); 6 | const positive: unknown = 0; 7 | const negative: unknown = ""; 8 | 9 | Deno.bench({ 10 | name: "current", 11 | fn() { 12 | assert(repeats.every(() => isNumber(positive))); 13 | }, 14 | group: "isNumber (positive)", 15 | }); 16 | 17 | Deno.bench({ 18 | name: "current", 19 | fn() { 20 | assert(repeats.every(() => !isNumber(negative))); 21 | }, 22 | group: "isNumber (negative)", 23 | }); 24 | 25 | Deno.bench({ 26 | name: "v4.2.0", 27 | fn() { 28 | assert(repeats.every(() => isNumber420(positive))); 29 | }, 30 | group: "isNumber (positive)", 31 | }); 32 | 33 | Deno.bench({ 34 | name: "v4.2.0", 35 | fn() { 36 | assert(repeats.every(() => !isNumber420(negative))); 37 | }, 38 | group: "isNumber (negative)", 39 | }); 40 | -------------------------------------------------------------------------------- /is/number_test.ts: -------------------------------------------------------------------------------- 1 | import { testWithExamples } from "../_testutil.ts"; 2 | import { isNumber } from "./number.ts"; 3 | 4 | Deno.test("isNumber", async (t) => { 5 | await testWithExamples(t, isNumber, { validExamples: ["number"] }); 6 | }); 7 | -------------------------------------------------------------------------------- /is/object_of.ts: -------------------------------------------------------------------------------- 1 | import type { FlatType } from "../_typeutil.ts"; 2 | import { rewriteName } from "../_funcutil.ts"; 3 | import { 4 | annotate, 5 | type AsOptional, 6 | type AsReadonly, 7 | type IsPredObj, 8 | } from "../_annotation.ts"; 9 | import type { Predicate } from "../type.ts"; 10 | 11 | /** 12 | * Return a type predicate function that returns `true` if the type of `x` is `ObjectOf`. 13 | * 14 | * Use {@linkcode [is/record-of].isRecordOf|isRecordOf} if you want to check if the type of `x` is a record of `T`. 15 | * 16 | * If {@linkcode [as/optional].asOptional|asOptional} is specified in the predicate function in `predObj`, the property becomes optional. 17 | * If {@linkcode [as/readonly].asReadonly|asReadonly} is specified in the predicate function in `predObj`, the property becomes readonly. 18 | * 19 | * The number of keys of `x` must be greater than or equal to the number of keys of `predObj`. 20 | * Use {@linkcode [is/strict-of].isStrictOf|isStrictOf} if you want to check the exact number of keys. 21 | * 22 | * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. 23 | * 24 | * ```ts 25 | * import { as, is } from "@core/unknownutil"; 26 | * 27 | * const isMyType = is.ObjectOf({ 28 | * a: is.Number, 29 | * b: is.String, 30 | * c: as.Optional(is.Boolean), 31 | * d: as.Readonly(is.String), 32 | * }); 33 | * const a: unknown = { a: 0, b: "a", d: "d" }; 34 | * if (isMyType(a)) { 35 | * const _: { a: number; b: string; c?: boolean | undefined, readonly d: string } = a; 36 | * } 37 | * ``` 38 | */ 39 | export function isObjectOf< 40 | T extends Record>, 41 | >(predObj: T): Predicate> & IsPredObj { 42 | const preds: readonly [key: PropertyKey, pred: Predicate][] = [ 43 | ...Object.keys(predObj), 44 | ...Object.getOwnPropertySymbols(predObj), 45 | ].map((k) => [k, predObj[k]]); 46 | const pred = rewriteName( 47 | (x): x is ObjectOf => { 48 | if (!isObject(x)) return false; 49 | return preds.every(([k, pred]) => pred(x[k])); 50 | }, 51 | "isObjectOf", 52 | predObj, 53 | ); 54 | return annotate(pred, "predObj", predObj); 55 | } 56 | 57 | function isObject(x: unknown): x is Record { 58 | if (x == null) return false; 59 | if (typeof x !== "object" && typeof x !== "function") return false; 60 | if (Array.isArray(x)) return false; 61 | return true; 62 | } 63 | 64 | type ObjectOf>> = FlatType< 65 | // Readonly/Optional 66 | & { 67 | readonly [ 68 | K in keyof T as T[K] extends AsReadonly 69 | ? T[K] extends AsOptional ? K : never 70 | : never 71 | ]?: T[K] extends Predicate ? U : never; 72 | } 73 | // Readonly/Non optional 74 | & { 75 | readonly [ 76 | K in keyof T as T[K] extends AsReadonly 77 | ? T[K] extends AsOptional ? never : K 78 | : never 79 | ]: T[K] extends Predicate ? U : never; 80 | } 81 | // Non readonly/Optional 82 | & { 83 | [ 84 | K in keyof T as T[K] extends AsReadonly ? never 85 | : T[K] extends AsOptional ? K 86 | : never 87 | ]?: T[K] extends Predicate ? U : never; 88 | } 89 | // Non readonly/Non optional 90 | & { 91 | [ 92 | K in keyof T as T[K] extends AsReadonly ? never 93 | : T[K] extends AsOptional ? never 94 | : K 95 | ]: T[K] extends Predicate ? U : never; 96 | } 97 | >; 98 | -------------------------------------------------------------------------------- /is/object_of_bench.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "@std/assert"; 2 | import { isObjectOf as isObjectOf420 } from "jsr:@core/unknownutil@4.2.0/is/object-of"; 3 | import { isObjectOf } from "./object_of.ts"; 4 | 5 | const repeats = Array.from({ length: 100 }); 6 | const positive: unknown = { a: "", b: 0 }; 7 | const positive10: unknown = Object.assign( 8 | { a: "", b: 0 }, 9 | Array.from({ length: 10 }), 10 | ); 11 | const positive1000: unknown = Object.assign( 12 | { a: "", b: 0 }, 13 | Array.from({ length: 1000 }), 14 | ); 15 | const negative: unknown = {}; 16 | const negative10: unknown = Object.assign( 17 | {}, 18 | Array.from({ length: 10 }), 19 | ); 20 | const negative1000: unknown = Object.assign( 21 | {}, 22 | Array.from({ length: 1000 }), 23 | ); 24 | 25 | const predObj = { 26 | a: (x: unknown): x is string => typeof x === "string", 27 | b: (x: unknown): x is number => typeof x === "number", 28 | }; 29 | const pred420 = isObjectOf420(predObj); 30 | const pred = isObjectOf(predObj); 31 | 32 | Deno.bench({ 33 | name: "current", 34 | fn() { 35 | assert(repeats.every(() => pred(positive))); 36 | }, 37 | group: "isObjectOf (positive)", 38 | }); 39 | 40 | Deno.bench({ 41 | name: "current", 42 | fn() { 43 | assert(repeats.every(() => pred(positive10))); 44 | }, 45 | group: "isObjectOf (positive 10)", 46 | }); 47 | 48 | Deno.bench({ 49 | name: "current", 50 | fn() { 51 | assert(repeats.every(() => pred(positive1000))); 52 | }, 53 | group: "isObjectOf (positive 1000)", 54 | }); 55 | 56 | Deno.bench({ 57 | name: "current", 58 | fn() { 59 | assert(repeats.every(() => !pred(negative))); 60 | }, 61 | group: "isObjectOf (negative)", 62 | }); 63 | 64 | Deno.bench({ 65 | name: "current", 66 | fn() { 67 | assert(repeats.every(() => !pred(negative10))); 68 | }, 69 | group: "isObjectOf (negative 10)", 70 | }); 71 | 72 | Deno.bench({ 73 | name: "current", 74 | fn() { 75 | assert(repeats.every(() => !pred(negative1000))); 76 | }, 77 | group: "isObjectOf (negative 1000)", 78 | }); 79 | 80 | Deno.bench({ 81 | name: "v4.2.0", 82 | fn() { 83 | assert(repeats.every(() => pred420(positive))); 84 | }, 85 | group: "isObjectOf (positive)", 86 | }); 87 | 88 | Deno.bench({ 89 | name: "v4.2.0", 90 | fn() { 91 | assert(repeats.every(() => pred420(positive10))); 92 | }, 93 | group: "isObjectOf (positive 10)", 94 | }); 95 | 96 | Deno.bench({ 97 | name: "v4.2.0", 98 | fn() { 99 | assert(repeats.every(() => pred420(positive1000))); 100 | }, 101 | group: "isObjectOf (positive 1000)", 102 | }); 103 | 104 | Deno.bench({ 105 | name: "v4.2.0", 106 | fn() { 107 | assert(repeats.every(() => !pred420(negative))); 108 | }, 109 | group: "isObjectOf (negative)", 110 | }); 111 | 112 | Deno.bench({ 113 | name: "v4.2.0", 114 | fn() { 115 | assert(repeats.every(() => !pred420(negative10))); 116 | }, 117 | group: "isObjectOf (negative 10)", 118 | }); 119 | 120 | Deno.bench({ 121 | name: "v4.2.0", 122 | fn() { 123 | assert(repeats.every(() => !pred420(negative1000))); 124 | }, 125 | group: "isObjectOf (negative 1000)", 126 | }); 127 | -------------------------------------------------------------------------------- /is/omit_of.ts: -------------------------------------------------------------------------------- 1 | import type { FlatType } from "../_typeutil.ts"; 2 | import type { IsPredObj } from "../_annotation.ts"; 3 | import type { Predicate } from "../type.ts"; 4 | import { isObjectOf } from "./object_of.ts"; 5 | 6 | /** 7 | * Return a type predicate function that returns `true` if the type of `x` is `Omit, K>`. 8 | * 9 | * It only supports modifing a predicate function annotated with `IsPredObj`, usually returned by the followings 10 | * 11 | * - {@linkcode [is/intersection-of].isIntersectionOf|isIntersectionOf} 12 | * - {@linkcode [is/object-of].isObjectOf|isObjectOf} 13 | * - {@linkcode [is/omit-of].isOmitOf|isOmitOf} 14 | * - {@linkcode [is/partial-of].isPartialOf|isPartialOf} 15 | * - {@linkcode [is/pick-of].isPickOf|isPickOf} 16 | * - {@linkcode [is/readonly-of].isReadonlyOf|isReadonlyOf} 17 | * - {@linkcode [is/required-of].isRequiredOf|isRequiredOf} 18 | * - {@linkcode [is/strict-of].isStrictOf|isStrictOf} 19 | * 20 | * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. 21 | * 22 | * ```typescript 23 | * import { as, is } from "@core/unknownutil"; 24 | * 25 | * const isMyType = is.OmitOf(is.ObjectOf({ 26 | * a: is.Number, 27 | * b: is.String, 28 | * c: as.Optional(is.Boolean), 29 | * }), ["a", "c"]); 30 | * const a: unknown = { a: 0, b: "a" }; 31 | * if (isMyType(a)) { 32 | * const _: { b: string } = a; 33 | * } 34 | * ``` 35 | */ 36 | export function isOmitOf< 37 | T extends Record, 38 | P extends Record>, 39 | K extends keyof T, 40 | >( 41 | pred: Predicate & IsPredObj

, 42 | keys: K[], 43 | ): 44 | & Predicate>> 45 | & IsPredObj

{ 46 | const predObj = { ...pred.predObj }; 47 | for (const key of keys) { 48 | delete predObj[key]; 49 | } 50 | return isObjectOf(predObj as Record>) as 51 | & Predicate>> 52 | & IsPredObj

; 53 | } 54 | -------------------------------------------------------------------------------- /is/omit_of_bench.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "@std/assert"; 2 | import { isObjectOf } from "./object_of.ts"; 3 | import { isOmitOf as isOmitOf420 } from "jsr:@core/unknownutil@4.2.0/is/omit-of"; 4 | import { isOmitOf } from "./omit_of.ts"; 5 | 6 | const repeats = Array.from({ length: 100 }); 7 | const positive: unknown = { a: "" }; 8 | const positive10: unknown = Object.assign( 9 | { a: "" }, 10 | Array.from({ length: 10 }), 11 | ); 12 | const positive1000: unknown = Object.assign( 13 | { a: "" }, 14 | Array.from({ length: 1000 }), 15 | ); 16 | const negative: unknown = {}; 17 | const negative10: unknown = Object.assign( 18 | {}, 19 | Array.from({ length: 10 }), 20 | ); 21 | const negative1000: unknown = Object.assign( 22 | {}, 23 | Array.from({ length: 1000 }), 24 | ); 25 | 26 | const predOrig = isObjectOf({ 27 | a: (x: unknown): x is string => typeof x === "string", 28 | b: (x: unknown): x is number => typeof x === "number", 29 | }); 30 | const pred420 = isOmitOf420(predOrig, ["b"]); 31 | const pred = isOmitOf(predOrig, ["b"]); 32 | 33 | Deno.bench({ 34 | name: "current", 35 | fn() { 36 | assert(repeats.every(() => pred(positive))); 37 | }, 38 | group: "isOmitOf (positive)", 39 | }); 40 | 41 | Deno.bench({ 42 | name: "current", 43 | fn() { 44 | assert(repeats.every(() => pred(positive10))); 45 | }, 46 | group: "isOmitOf (positive 10)", 47 | }); 48 | 49 | Deno.bench({ 50 | name: "current", 51 | fn() { 52 | assert(repeats.every(() => pred(positive1000))); 53 | }, 54 | group: "isOmitOf (positive 1000)", 55 | }); 56 | 57 | Deno.bench({ 58 | name: "current", 59 | fn() { 60 | assert(repeats.every(() => !pred(negative))); 61 | }, 62 | group: "isOmitOf (negative)", 63 | }); 64 | 65 | Deno.bench({ 66 | name: "current", 67 | fn() { 68 | assert(repeats.every(() => !pred(negative10))); 69 | }, 70 | group: "isOmitOf (negative 10)", 71 | }); 72 | 73 | Deno.bench({ 74 | name: "current", 75 | fn() { 76 | assert(repeats.every(() => !pred(negative1000))); 77 | }, 78 | group: "isOmitOf (negative 1000)", 79 | }); 80 | 81 | Deno.bench({ 82 | name: "v4.2.0", 83 | fn() { 84 | assert(repeats.every(() => pred420(positive))); 85 | }, 86 | group: "isOmitOf (positive)", 87 | }); 88 | 89 | Deno.bench({ 90 | name: "v4.2.0", 91 | fn() { 92 | assert(repeats.every(() => pred420(positive10))); 93 | }, 94 | group: "isOmitOf (positive 10)", 95 | }); 96 | 97 | Deno.bench({ 98 | name: "v4.2.0", 99 | fn() { 100 | assert(repeats.every(() => pred420(positive1000))); 101 | }, 102 | group: "isOmitOf (positive 1000)", 103 | }); 104 | 105 | Deno.bench({ 106 | name: "v4.2.0", 107 | fn() { 108 | assert(repeats.every(() => !pred420(negative))); 109 | }, 110 | group: "isOmitOf (negative)", 111 | }); 112 | 113 | Deno.bench({ 114 | name: "v4.2.0", 115 | fn() { 116 | assert(repeats.every(() => !pred420(negative10))); 117 | }, 118 | group: "isOmitOf (negative 10)", 119 | }); 120 | 121 | Deno.bench({ 122 | name: "v4.2.0", 123 | fn() { 124 | assert(repeats.every(() => !pred420(negative1000))); 125 | }, 126 | group: "isOmitOf (negative 1000)", 127 | }); 128 | -------------------------------------------------------------------------------- /is/omit_of_test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from "@std/assert"; 2 | import { assertSnapshot } from "@std/testing/snapshot"; 3 | import { assertType, type IsExact } from "@std/testing/types"; 4 | import { is } from "./mod.ts"; 5 | import { isOmitOf } from "./omit_of.ts"; 6 | 7 | Deno.test("isOmitOf", async (t) => { 8 | const pred = is.ObjectOf({ 9 | a: is.Number, 10 | b: is.String, 11 | c: is.Boolean, 12 | }); 13 | 14 | await t.step("returns properly named predicate function", async (t) => { 15 | assertEquals(typeof isOmitOf(pred, ["b"]), "function"); 16 | await assertSnapshot(t, isOmitOf(pred, ["b"]).name); 17 | await assertSnapshot(t, isOmitOf(isOmitOf(pred, ["b"]), ["c"]).name); 18 | }); 19 | 20 | await t.step("returns true on Omit object", () => { 21 | assertEquals( 22 | isOmitOf(pred, ["b"])({ a: 0, b: undefined, c: true }), 23 | true, 24 | ); 25 | assertEquals(isOmitOf(pred, ["b", "c"])({ a: 0 }), true); 26 | }); 27 | 28 | await t.step("returns false on non Omit object", () => { 29 | assertEquals( 30 | isOmitOf(pred, ["b"])("a"), 31 | false, 32 | "Value is not an object", 33 | ); 34 | assertEquals( 35 | isOmitOf(pred, ["b"])({ a: 0, b: "a", c: "" }), 36 | false, 37 | "Object have a different type property", 38 | ); 39 | }); 40 | 41 | await t.step("predicated type is correct", () => { 42 | const a: unknown = { a: 0, b: "a", c: true }; 43 | 44 | if (isOmitOf(pred, ["b"])(a)) { 45 | assertType< 46 | IsExact 47 | >(true); 48 | } 49 | 50 | if (isOmitOf(isOmitOf(pred, ["b"]), ["c"])(a)) { 51 | assertType< 52 | IsExact 53 | >(true); 54 | } 55 | }); 56 | 57 | await t.step("with symbol properties", async (t) => { 58 | const b = Symbol("b"); 59 | const c = Symbol("c"); 60 | const pred = is.ObjectOf({ 61 | a: is.Number, 62 | [b]: is.String, 63 | [c]: is.Boolean, 64 | }); 65 | 66 | await t.step("returns properly named predicate function", async (t) => { 67 | assertEquals(typeof isOmitOf(pred, [b]), "function"); 68 | await assertSnapshot(t, isOmitOf(pred, [b]).name); 69 | await assertSnapshot(t, isOmitOf(isOmitOf(pred, [b]), [c]).name); 70 | }); 71 | 72 | await t.step("returns true on Omit object", () => { 73 | assertEquals( 74 | isOmitOf(pred, [b])({ a: 0, [b]: undefined, [c]: true }), 75 | true, 76 | ); 77 | assertEquals(isOmitOf(pred, [b, c])({ a: 0 }), true); 78 | }); 79 | 80 | await t.step("returns false on non Omit object", () => { 81 | assertEquals( 82 | isOmitOf(pred, [b])("a"), 83 | false, 84 | "Value is not an object", 85 | ); 86 | assertEquals( 87 | isOmitOf(pred, [b])({ a: 0, [b]: "a", [c]: "" }), 88 | false, 89 | "Object have a different type property", 90 | ); 91 | }); 92 | 93 | await t.step("predicated type is correct", () => { 94 | const x: unknown = { a: 0, [b]: "a", [c]: true }; 95 | 96 | if (isOmitOf(pred, [b])(x)) { 97 | assertType< 98 | IsExact 99 | >(true); 100 | } 101 | 102 | if (isOmitOf(isOmitOf(pred, [b]), [c])(x)) { 103 | assertType< 104 | IsExact 105 | >(true); 106 | } 107 | }); 108 | }); 109 | }); 110 | -------------------------------------------------------------------------------- /is/parameters_of_bench.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "@std/assert"; 2 | import { isParametersOf as isParametersOf420 } from "jsr:@core/unknownutil@4.2.0/is/parameters-of"; 3 | import { isParametersOf } from "./parameters_of.ts"; 4 | 5 | const repeats = Array.from({ length: 100 }); 6 | const positive: unknown = ["", 0]; 7 | const positive10: unknown = ["", 0, ...Array.from({ length: 10 })]; 8 | const positive1000: unknown = ["", 0, ...Array.from({ length: 1000 })]; 9 | const negative: unknown = []; 10 | const negative10: unknown = Array.from({ length: 10 }); 11 | const negative1000: unknown = Array.from({ length: 1000 }); 12 | 13 | const predTup = [ 14 | (x: unknown): x is string => typeof x === "string", 15 | (x: unknown): x is number => typeof x === "number", 16 | ] as const; 17 | const predElse = (_x: unknown): _x is number[] => true; 18 | const pred420 = isParametersOf420(predTup, predElse); 19 | const pred = isParametersOf(predTup, predElse); 20 | 21 | Deno.bench({ 22 | name: "current", 23 | fn() { 24 | assert(repeats.every(() => pred(positive))); 25 | }, 26 | group: "isParametersOf (positive)", 27 | }); 28 | 29 | Deno.bench({ 30 | name: "current", 31 | fn() { 32 | assert(repeats.every(() => pred(positive10))); 33 | }, 34 | group: "isParametersOf (positive 10)", 35 | }); 36 | 37 | Deno.bench({ 38 | name: "current", 39 | fn() { 40 | assert(repeats.every(() => pred(positive1000))); 41 | }, 42 | group: "isParametersOf (positive 1000)", 43 | }); 44 | 45 | Deno.bench({ 46 | name: "current", 47 | fn() { 48 | assert(repeats.every(() => !pred(negative))); 49 | }, 50 | group: "isParametersOf (negative)", 51 | }); 52 | 53 | Deno.bench({ 54 | name: "current", 55 | fn() { 56 | assert(repeats.every(() => !pred(negative10))); 57 | }, 58 | group: "isParametersOf (negative 10)", 59 | }); 60 | 61 | Deno.bench({ 62 | name: "current", 63 | fn() { 64 | assert(repeats.every(() => !pred(negative1000))); 65 | }, 66 | group: "isParametersOf (negative 1000)", 67 | }); 68 | 69 | Deno.bench({ 70 | name: "v4.2.0", 71 | fn() { 72 | assert(repeats.every(() => pred420(positive))); 73 | }, 74 | group: "isParametersOf (positive)", 75 | }); 76 | 77 | Deno.bench({ 78 | name: "v4.2.0", 79 | fn() { 80 | assert(repeats.every(() => pred420(positive10))); 81 | }, 82 | group: "isParametersOf (positive 10)", 83 | }); 84 | 85 | Deno.bench({ 86 | name: "v4.2.0", 87 | fn() { 88 | assert(repeats.every(() => pred420(positive1000))); 89 | }, 90 | group: "isParametersOf (positive 1000)", 91 | }); 92 | 93 | Deno.bench({ 94 | name: "v4.2.0", 95 | fn() { 96 | assert(repeats.every(() => !pred420(negative))); 97 | }, 98 | group: "isParametersOf (negative)", 99 | }); 100 | 101 | Deno.bench({ 102 | name: "v4.2.0", 103 | fn() { 104 | assert(repeats.every(() => !pred420(negative10))); 105 | }, 106 | group: "isParametersOf (negative 10)", 107 | }); 108 | 109 | Deno.bench({ 110 | name: "v4.2.0", 111 | fn() { 112 | assert(repeats.every(() => !pred420(negative1000))); 113 | }, 114 | group: "isParametersOf (negative 1000)", 115 | }); 116 | -------------------------------------------------------------------------------- /is/partial_of.ts: -------------------------------------------------------------------------------- 1 | import type { FlatType } from "../_typeutil.ts"; 2 | import type { IsPredObj } from "../_annotation.ts"; 3 | import { asOptional } from "../as/optional.ts"; 4 | import type { Predicate } from "../type.ts"; 5 | import { isObjectOf } from "./object_of.ts"; 6 | 7 | /** 8 | * Return a type predicate function that returns `true` if the type of `x` is `Partial>`. 9 | * 10 | * It only supports modifing a predicate function annotated with `IsPredObj`, usually returned by the followings 11 | * 12 | * - {@linkcode [is/intersection-of].isIntersectionOf|isIntersectionOf} 13 | * - {@linkcode [is/object-of].isObjectOf|isObjectOf} 14 | * - {@linkcode [is/omit-of].isOmitOf|isOmitOf} 15 | * - {@linkcode [is/partial-of].isPartialOf|isPartialOf} 16 | * - {@linkcode [is/pick-of].isPickOf|isPickOf} 17 | * - {@linkcode [is/readonly-of].isReadonlyOf|isReadonlyOf} 18 | * - {@linkcode [is/required-of].isRequiredOf|isRequiredOf} 19 | * - {@linkcode [is/strict-of].isStrictOf|isStrictOf} 20 | * 21 | * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. 22 | * 23 | * ```typescript 24 | * import { as, is } from "@core/unknownutil"; 25 | * 26 | * const isMyType = is.PartialOf(is.ObjectOf({ 27 | * a: is.Number, 28 | * b: is.UnionOf([is.String, is.Undefined]), 29 | * c: as.Optional(is.Boolean), 30 | * })); 31 | * const a: unknown = { a: undefined, other: "other" }; 32 | * if (isMyType(a)) { 33 | * const _: { a?: number | undefined; b?: string | undefined; c?: boolean | undefined } = a; 34 | * } 35 | * ``` 36 | */ 37 | export function isPartialOf< 38 | T extends Record, 39 | P extends Record>, 40 | >( 41 | pred: Predicate & IsPredObj

, 42 | ): 43 | & Predicate>> 44 | & IsPredObj

{ 45 | const keys = [ 46 | ...Object.keys(pred.predObj), 47 | ...Object.getOwnPropertySymbols(pred.predObj), 48 | ]; 49 | const predObj: Record> = { ...pred.predObj }; 50 | for (const key of keys) { 51 | predObj[key] = asOptional(predObj[key]); 52 | } 53 | return isObjectOf(predObj) as 54 | & Predicate>> 55 | & IsPredObj

; 56 | } 57 | -------------------------------------------------------------------------------- /is/partial_of_bench.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "@std/assert"; 2 | import { isObjectOf } from "./object_of.ts"; 3 | import { isPartialOf as isPartialOf420 } from "jsr:@core/unknownutil@4.2.0/is/partial-of"; 4 | import { isPartialOf } from "./partial_of.ts"; 5 | 6 | const repeats = Array.from({ length: 100 }); 7 | const positive: unknown = { a: "" }; 8 | const positive10: unknown = Object.assign( 9 | { a: "" }, 10 | Array.from({ length: 10 }), 11 | ); 12 | const positive1000: unknown = Object.assign( 13 | { a: "" }, 14 | Array.from({ length: 1000 }), 15 | ); 16 | const negative: unknown = []; 17 | const negative10: unknown = Object.assign( 18 | [], 19 | Array.from({ length: 10 }), 20 | ); 21 | const negative1000: unknown = Object.assign( 22 | [], 23 | Array.from({ length: 1000 }), 24 | ); 25 | 26 | const predOrig = isObjectOf({ 27 | a: (x: unknown): x is string => typeof x === "string", 28 | b: (x: unknown): x is number => typeof x === "number", 29 | }); 30 | const pred420 = isPartialOf420(predOrig); 31 | const pred = isPartialOf(predOrig); 32 | 33 | Deno.bench({ 34 | name: "current", 35 | fn() { 36 | assert(repeats.every(() => pred(positive))); 37 | }, 38 | group: "isPartialOf (positive)", 39 | }); 40 | 41 | Deno.bench({ 42 | name: "current", 43 | fn() { 44 | assert(repeats.every(() => pred(positive10))); 45 | }, 46 | group: "isPartialOf (positive 10)", 47 | }); 48 | 49 | Deno.bench({ 50 | name: "current", 51 | fn() { 52 | assert(repeats.every(() => pred(positive1000))); 53 | }, 54 | group: "isPartialOf (positive 1000)", 55 | }); 56 | 57 | Deno.bench({ 58 | name: "current", 59 | fn() { 60 | assert(repeats.every(() => !pred(negative))); 61 | }, 62 | group: "isPartialOf (negative)", 63 | }); 64 | 65 | Deno.bench({ 66 | name: "current", 67 | fn() { 68 | assert(repeats.every(() => !pred(negative10))); 69 | }, 70 | group: "isPartialOf (negative 10)", 71 | }); 72 | 73 | Deno.bench({ 74 | name: "current", 75 | fn() { 76 | assert(repeats.every(() => !pred(negative1000))); 77 | }, 78 | group: "isPartialOf (negative 1000)", 79 | }); 80 | 81 | Deno.bench({ 82 | name: "v4.2.0", 83 | fn() { 84 | assert(repeats.every(() => pred420(positive))); 85 | }, 86 | group: "isPartialOf (positive)", 87 | }); 88 | 89 | Deno.bench({ 90 | name: "v4.2.0", 91 | fn() { 92 | assert(repeats.every(() => pred420(positive10))); 93 | }, 94 | group: "isPartialOf (positive 10)", 95 | }); 96 | 97 | Deno.bench({ 98 | name: "v4.2.0", 99 | fn() { 100 | assert(repeats.every(() => pred420(positive1000))); 101 | }, 102 | group: "isPartialOf (positive 1000)", 103 | }); 104 | 105 | Deno.bench({ 106 | name: "v4.2.0", 107 | fn() { 108 | assert(repeats.every(() => !pred420(negative))); 109 | }, 110 | group: "isPartialOf (negative)", 111 | }); 112 | 113 | Deno.bench({ 114 | name: "v4.2.0", 115 | fn() { 116 | assert(repeats.every(() => !pred420(negative10))); 117 | }, 118 | group: "isPartialOf (negative 10)", 119 | }); 120 | 121 | Deno.bench({ 122 | name: "v4.2.0", 123 | fn() { 124 | assert(repeats.every(() => !pred420(negative1000))); 125 | }, 126 | group: "isPartialOf (negative 1000)", 127 | }); 128 | -------------------------------------------------------------------------------- /is/partial_of_test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from "@std/assert"; 2 | import { assertSnapshot } from "@std/testing/snapshot"; 3 | import { assertType, type IsExact } from "@std/testing/types"; 4 | import { as } from "../as/mod.ts"; 5 | import { is } from "./mod.ts"; 6 | import { isPartialOf } from "./partial_of.ts"; 7 | 8 | Deno.test("isPartialOf", async (t) => { 9 | const pred = is.ObjectOf({ 10 | a: is.Number, 11 | b: is.UnionOf([is.String, is.Undefined]), 12 | c: as.Optional(is.Boolean), 13 | d: as.Readonly(is.String), 14 | }); 15 | 16 | await t.step("returns properly named predicate function", async (t) => { 17 | await assertSnapshot(t, isPartialOf(pred).name); 18 | await assertSnapshot(t, isPartialOf(isPartialOf(pred)).name); 19 | }); 20 | 21 | await t.step("returns true on Partial object", () => { 22 | assertEquals( 23 | isPartialOf(pred)({ a: undefined, b: undefined, c: undefined }), 24 | true, 25 | ); 26 | assertEquals(isPartialOf(pred)({}), true); 27 | }); 28 | 29 | await t.step("returns false on non Partial object", () => { 30 | assertEquals(isPartialOf(pred)("a"), false, "Value is not an object"); 31 | assertEquals( 32 | isPartialOf(pred)({ a: 0, b: "a", c: "" }), 33 | false, 34 | "Object have a different type property", 35 | ); 36 | }); 37 | 38 | await t.step("predicated type is correct", () => { 39 | const a: unknown = { a: 0, b: "a", c: true }; 40 | if (isPartialOf(pred)(a)) { 41 | assertType< 42 | IsExact< 43 | typeof a, 44 | Partial< 45 | { 46 | a: number; 47 | b: string | undefined; 48 | c: boolean | undefined; 49 | readonly d: string; 50 | } 51 | > 52 | > 53 | >(true); 54 | } 55 | }); 56 | 57 | await t.step("with symbol properties", async (t) => { 58 | const b = Symbol("b"); 59 | const c = Symbol("c"); 60 | const d = Symbol("c"); 61 | const pred = is.ObjectOf({ 62 | a: is.Number, 63 | [b]: is.UnionOf([is.String, is.Undefined]), 64 | [c]: as.Optional(is.Boolean), 65 | [d]: as.Readonly(is.String), 66 | }); 67 | 68 | await t.step("returns properly named predicate function", async (t) => { 69 | await assertSnapshot(t, isPartialOf(pred).name); 70 | await assertSnapshot(t, isPartialOf(isPartialOf(pred)).name); 71 | }); 72 | 73 | await t.step("returns true on Partial object", () => { 74 | assertEquals( 75 | isPartialOf(pred)({ a: undefined, [b]: undefined, [c]: undefined }), 76 | true, 77 | ); 78 | assertEquals(isPartialOf(pred)({}), true); 79 | }); 80 | 81 | await t.step("returns false on non Partial object", () => { 82 | assertEquals(isPartialOf(pred)("a"), false, "Value is not an object"); 83 | assertEquals( 84 | isPartialOf(pred)({ a: 0, [b]: "a", [c]: "" }), 85 | false, 86 | "Object have a different type property", 87 | ); 88 | }); 89 | 90 | await t.step("predicated type is correct", () => { 91 | const a: unknown = { a: 0, [b]: "a", [c]: true }; 92 | if (isPartialOf(pred)(a)) { 93 | assertType< 94 | IsExact< 95 | typeof a, 96 | Partial<{ 97 | a: number; 98 | [b]: string | undefined; 99 | [c]: boolean | undefined; 100 | readonly [d]: string; 101 | }> 102 | > 103 | >(true); 104 | } 105 | }); 106 | }); 107 | }); 108 | -------------------------------------------------------------------------------- /is/pick_of.ts: -------------------------------------------------------------------------------- 1 | import type { FlatType } from "../_typeutil.ts"; 2 | import type { IsPredObj } from "../_annotation.ts"; 3 | import type { Predicate } from "../type.ts"; 4 | import { isObjectOf } from "./object_of.ts"; 5 | 6 | /** 7 | * Return a type predicate function that returns `true` if the type of `x` is `Pick, K>`. 8 | * 9 | * It only supports modifing a predicate function annotated with `IsPredObj`, usually returned by the followings 10 | * 11 | * - {@linkcode [is/intersection-of].isIntersectionOf|isIntersectionOf} 12 | * - {@linkcode [is/object-of].isObjectOf|isObjectOf} 13 | * - {@linkcode [is/omit-of].isOmitOf|isOmitOf} 14 | * - {@linkcode [is/partial-of].isPartialOf|isPartialOf} 15 | * - {@linkcode [is/pick-of].isPickOf|isPickOf} 16 | * - {@linkcode [is/readonly-of].isReadonlyOf|isReadonlyOf} 17 | * - {@linkcode [is/required-of].isRequiredOf|isRequiredOf} 18 | * - {@linkcode [is/strict-of].isStrictOf|isStrictOf} 19 | * 20 | * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. 21 | * 22 | * ```typescript 23 | * import { as, is } from "@core/unknownutil"; 24 | * 25 | * const isMyType = is.PickOf(is.ObjectOf({ 26 | * a: is.Number, 27 | * b: is.String, 28 | * c: as.Optional(is.Boolean), 29 | * }), ["a", "c"]); 30 | * const a: unknown = { a: 0, b: "a" }; 31 | * if (isMyType(a)) { 32 | * const _: { a: number; c?: boolean | undefined } = a; 33 | * } 34 | * ``` 35 | */ 36 | export function isPickOf< 37 | T extends Record, 38 | P extends Record>, 39 | K extends keyof T, 40 | >( 41 | pred: Predicate & IsPredObj

, 42 | keys: K[], 43 | ): 44 | & Predicate>> 45 | & IsPredObj

{ 46 | const omitKeys = new Set([ 47 | ...Object.keys(pred.predObj), 48 | ...Object.getOwnPropertySymbols(pred.predObj), 49 | ]).difference(new Set(keys)); 50 | const predObj = { ...pred.predObj }; 51 | for (const key of omitKeys) { 52 | delete predObj[key]; 53 | } 54 | return isObjectOf(predObj as Record>) as 55 | & Predicate>> 56 | & IsPredObj

; 57 | } 58 | -------------------------------------------------------------------------------- /is/pick_of_bench.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "@std/assert"; 2 | import { isObjectOf } from "./object_of.ts"; 3 | import { isPickOf as isPickOf420 } from "jsr:@core/unknownutil@4.2.0/is/pick-of"; 4 | import { isPickOf } from "./pick_of.ts"; 5 | 6 | const repeats = Array.from({ length: 100 }); 7 | const positive: unknown = { a: "" }; 8 | const positive10: unknown = Object.assign( 9 | { a: "" }, 10 | Array.from({ length: 10 }), 11 | ); 12 | const positive1000: unknown = Object.assign( 13 | { a: "" }, 14 | Array.from({ length: 1000 }), 15 | ); 16 | const negative: unknown = {}; 17 | const negative10: unknown = Object.assign( 18 | {}, 19 | Array.from({ length: 10 }), 20 | ); 21 | const negative1000: unknown = Object.assign( 22 | {}, 23 | Array.from({ length: 1000 }), 24 | ); 25 | 26 | const predOrig = isObjectOf({ 27 | a: (x: unknown): x is string => typeof x === "string", 28 | b: (x: unknown): x is number => typeof x === "number", 29 | }); 30 | const pred420 = isPickOf420(predOrig, ["a"]); 31 | const pred = isPickOf(predOrig, ["a"]); 32 | 33 | Deno.bench({ 34 | name: "current", 35 | fn() { 36 | assert(repeats.every(() => pred(positive))); 37 | }, 38 | group: "isPickOf (positive)", 39 | }); 40 | 41 | Deno.bench({ 42 | name: "current", 43 | fn() { 44 | assert(repeats.every(() => pred(positive10))); 45 | }, 46 | group: "isPickOf (positive 10)", 47 | }); 48 | 49 | Deno.bench({ 50 | name: "current", 51 | fn() { 52 | assert(repeats.every(() => pred(positive1000))); 53 | }, 54 | group: "isPickOf (positive 1000)", 55 | }); 56 | 57 | Deno.bench({ 58 | name: "current", 59 | fn() { 60 | assert(repeats.every(() => !pred(negative))); 61 | }, 62 | group: "isPickOf (negative)", 63 | }); 64 | 65 | Deno.bench({ 66 | name: "current", 67 | fn() { 68 | assert(repeats.every(() => !pred(negative10))); 69 | }, 70 | group: "isPickOf (negative 10)", 71 | }); 72 | 73 | Deno.bench({ 74 | name: "current", 75 | fn() { 76 | assert(repeats.every(() => !pred(negative1000))); 77 | }, 78 | group: "isPickOf (negative 1000)", 79 | }); 80 | 81 | Deno.bench({ 82 | name: "v4.2.0", 83 | fn() { 84 | assert(repeats.every(() => pred420(positive))); 85 | }, 86 | group: "isPickOf (positive)", 87 | }); 88 | 89 | Deno.bench({ 90 | name: "v4.2.0", 91 | fn() { 92 | assert(repeats.every(() => pred420(positive10))); 93 | }, 94 | group: "isPickOf (positive 10)", 95 | }); 96 | 97 | Deno.bench({ 98 | name: "v4.2.0", 99 | fn() { 100 | assert(repeats.every(() => pred420(positive1000))); 101 | }, 102 | group: "isPickOf (positive 1000)", 103 | }); 104 | 105 | Deno.bench({ 106 | name: "v4.2.0", 107 | fn() { 108 | assert(repeats.every(() => !pred420(negative))); 109 | }, 110 | group: "isPickOf (negative)", 111 | }); 112 | 113 | Deno.bench({ 114 | name: "v4.2.0", 115 | fn() { 116 | assert(repeats.every(() => !pred420(negative10))); 117 | }, 118 | group: "isPickOf (negative 10)", 119 | }); 120 | 121 | Deno.bench({ 122 | name: "v4.2.0", 123 | fn() { 124 | assert(repeats.every(() => !pred420(negative1000))); 125 | }, 126 | group: "isPickOf (negative 1000)", 127 | }); 128 | -------------------------------------------------------------------------------- /is/pick_of_test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from "@std/assert"; 2 | import { assertSnapshot } from "@std/testing/snapshot"; 3 | import { assertType, type IsExact } from "@std/testing/types"; 4 | import { is } from "./mod.ts"; 5 | import { isPickOf } from "./pick_of.ts"; 6 | 7 | Deno.test("isPickOf", async (t) => { 8 | const pred = is.ObjectOf({ 9 | a: is.Number, 10 | b: is.String, 11 | c: is.Boolean, 12 | }); 13 | 14 | await t.step("returns properly named predicate function", async (t) => { 15 | await assertSnapshot(t, isPickOf(pred, ["a", "c"]).name); 16 | await assertSnapshot(t, isPickOf(isPickOf(pred, ["a", "c"]), ["a"]).name); 17 | }); 18 | 19 | await t.step("returns true on Pick object", () => { 20 | assertEquals( 21 | isPickOf(pred, ["a", "c"])({ a: 0, b: undefined, c: true }), 22 | true, 23 | ); 24 | assertEquals(isPickOf(pred, ["a"])({ a: 0 }), true); 25 | }); 26 | 27 | await t.step("returns false on non Pick object", () => { 28 | assertEquals( 29 | isPickOf(pred, ["a", "c"])("a"), 30 | false, 31 | "Value is not an object", 32 | ); 33 | assertEquals( 34 | isPickOf(pred, ["a", "c"])({ a: 0, b: "a", c: "" }), 35 | false, 36 | "Object have a different type property", 37 | ); 38 | }); 39 | 40 | await t.step("predicated type is correct", () => { 41 | const a: unknown = { a: 0, b: "a", c: true }; 42 | if (isPickOf(pred, ["a", "c"])(a)) { 43 | assertType< 44 | IsExact 45 | >(true); 46 | } 47 | if (isPickOf(isPickOf(pred, ["a", "c"]), ["a"])(a)) { 48 | assertType< 49 | IsExact 50 | >(true); 51 | } 52 | }); 53 | 54 | await t.step("with symbol properties", async (t) => { 55 | const b = Symbol("b"); 56 | const c = Symbol("c"); 57 | const pred = is.ObjectOf({ 58 | a: is.Number, 59 | [b]: is.String, 60 | [c]: is.Boolean, 61 | }); 62 | 63 | await t.step("returns properly named predicate function", async (t) => { 64 | await assertSnapshot(t, isPickOf(pred, ["a", c]).name); 65 | await assertSnapshot(t, isPickOf(isPickOf(pred, ["a", c]), [c]).name); 66 | }); 67 | 68 | await t.step("returns true on Pick object", () => { 69 | assertEquals( 70 | isPickOf(pred, ["a", c])({ a: 0, [b]: undefined, [c]: true }), 71 | true, 72 | ); 73 | assertEquals(isPickOf(pred, ["a"])({ a: 0 }), true); 74 | }); 75 | 76 | await t.step("returns false on non Pick object", () => { 77 | assertEquals( 78 | isPickOf(pred, ["a", c])("a"), 79 | false, 80 | "Value is not an object", 81 | ); 82 | assertEquals( 83 | isPickOf(pred, ["a", c])({ a: 0, [b]: "a", [c]: "" }), 84 | false, 85 | "Object have a different type property", 86 | ); 87 | }); 88 | 89 | await t.step("predicated type is correct", () => { 90 | const a: unknown = { a: 0, [b]: "a", [c]: true }; 91 | if (isPickOf(pred, ["a", c])(a)) { 92 | assertType< 93 | IsExact 94 | >(true); 95 | } 96 | if (isPickOf(isPickOf(pred, ["a", c]), ["a"])(a)) { 97 | assertType< 98 | IsExact 99 | >(true); 100 | } 101 | }); 102 | }); 103 | }); 104 | -------------------------------------------------------------------------------- /is/primitive.ts: -------------------------------------------------------------------------------- 1 | import type { Primitive } from "../type.ts"; 2 | 3 | const primitiveSet: Set = new Set([ 4 | "string", 5 | "number", 6 | "bigint", 7 | "boolean", 8 | "symbol", 9 | ]); 10 | 11 | /** 12 | * Return `true` if the type of `x` is `Primitive`. 13 | * 14 | * ```ts 15 | * import { is, type Primitive } from "@core/unknownutil"; 16 | * 17 | * const a: unknown = 0; 18 | * if (is.Primitive(a)) { 19 | * const _: Primitive = a; 20 | * } 21 | * ``` 22 | */ 23 | export function isPrimitive(x: unknown): x is Primitive { 24 | return x == null || primitiveSet.has(typeof x); 25 | } 26 | -------------------------------------------------------------------------------- /is/primitive_bench.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "@std/assert"; 2 | import { isPrimitive as isPrimitive420 } from "jsr:@core/unknownutil@4.2.0/is/primitive"; 3 | import { isPrimitive } from "./primitive.ts"; 4 | 5 | const repeats = Array.from({ length: 100 }); 6 | const positive: unknown = 0; 7 | const negative: unknown = {}; 8 | 9 | Deno.bench({ 10 | name: "current", 11 | fn() { 12 | assert(repeats.every(() => isPrimitive(positive))); 13 | }, 14 | group: "isPrimitive (positive)", 15 | }); 16 | 17 | Deno.bench({ 18 | name: "current", 19 | fn() { 20 | assert(repeats.every(() => !isPrimitive(negative))); 21 | }, 22 | group: "isPrimitive (negative)", 23 | }); 24 | 25 | Deno.bench({ 26 | name: "v4.2.0", 27 | fn() { 28 | assert(repeats.every(() => isPrimitive420(positive))); 29 | }, 30 | group: "isPrimitive (positive)", 31 | }); 32 | 33 | Deno.bench({ 34 | name: "v4.2.0", 35 | fn() { 36 | assert(repeats.every(() => !isPrimitive420(negative))); 37 | }, 38 | group: "isPrimitive (negative)", 39 | }); 40 | -------------------------------------------------------------------------------- /is/primitive_test.ts: -------------------------------------------------------------------------------- 1 | import { testWithExamples } from "../_testutil.ts"; 2 | import { isPrimitive } from "./primitive.ts"; 3 | 4 | Deno.test("isPrimitive", async (t) => { 5 | await testWithExamples(t, isPrimitive, { 6 | validExamples: [ 7 | "string", 8 | "number", 9 | "bigint", 10 | "boolean", 11 | "null", 12 | "undefined", 13 | "symbol", 14 | ], 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /is/readonly_of.ts: -------------------------------------------------------------------------------- 1 | import { rewriteName } from "../_funcutil.ts"; 2 | import type { IsPredObj } from "../_annotation.ts"; 3 | import type { Predicate } from "../type.ts"; 4 | 5 | /** 6 | * Return a type predicate function that returns `true` if the type of `x` is `Readonly>`. 7 | * 8 | * It only supports modifing a predicate function annotated with `IsPredObj`, usually returned by the followings 9 | * 10 | * - {@linkcode [is/intersection-of].isIntersectionOf|isIntersectionOf} 11 | * - {@linkcode [is/object-of].isObjectOf|isObjectOf} 12 | * - {@linkcode [is/omit-of].isOmitOf|isOmitOf} 13 | * - {@linkcode [is/partial-of].isPartialOf|isPartialOf} 14 | * - {@linkcode [is/pick-of].isPickOf|isPickOf} 15 | * - {@linkcode [is/readonly-of].isReadonlyOf|isReadonlyOf} 16 | * - {@linkcode [is/required-of].isRequiredOf|isRequiredOf} 17 | * - {@linkcode [is/strict-of].isStrictOf|isStrictOf} 18 | * 19 | * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. 20 | * 21 | * ```typescript 22 | * import { as, is } from "@core/unknownutil"; 23 | * 24 | * const isMyType = is.ReadonlyOf(is.ObjectOf({ 25 | * a: is.Number, 26 | * b: is.UnionOf([is.String, is.Undefined]), 27 | * c: as.Readonly(is.Boolean), 28 | * })); 29 | * const a: unknown = { a: 0, b: "b", c: true }; 30 | * if (isMyType(a)) { 31 | * const _: { readonly a: number; readonly b: string | undefined; readonly c: boolean } = a; 32 | * } 33 | * ``` 34 | */ 35 | export function isReadonlyOf< 36 | T extends Record, 37 | P extends Record>, 38 | >( 39 | pred: Predicate & IsPredObj

, 40 | ): 41 | & Predicate> 42 | & IsPredObj

; 43 | export function isReadonlyOf< 44 | T extends Record, 45 | >( 46 | pred: Predicate, 47 | ): Predicate>; 48 | export function isReadonlyOf< 49 | T extends readonly [unknown, ...unknown[]], 50 | >( 51 | pred: Predicate, 52 | ): Predicate>; 53 | export function isReadonlyOf( 54 | pred: Predicate, 55 | ): Predicate> { 56 | if (pred.name.startsWith("isReadonlyOf(")) return pred; 57 | return rewriteName((x) => pred(x), "isReadonlyOf", pred); 58 | } 59 | -------------------------------------------------------------------------------- /is/readonly_of_bench.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "@std/assert"; 2 | import { isObjectOf } from "./object_of.ts"; 3 | import { isReadonlyOf as isReadonlyOf420 } from "jsr:@core/unknownutil@4.2.0/is/readonly-of"; 4 | import { isReadonlyOf } from "./readonly_of.ts"; 5 | 6 | const repeats = Array.from({ length: 100 }); 7 | const positive: unknown = { a: "", b: 0 }; 8 | const positive10: unknown = Object.assign( 9 | { a: "", b: 0 }, 10 | Array.from({ length: 10 }), 11 | ); 12 | const positive1000: unknown = Object.assign( 13 | { a: "", b: 0 }, 14 | Array.from({ length: 1000 }), 15 | ); 16 | const negative: unknown = {}; 17 | const negative10: unknown = Object.assign( 18 | {}, 19 | Array.from({ length: 10 }), 20 | ); 21 | const negative1000: unknown = Object.assign( 22 | {}, 23 | Array.from({ length: 1000 }), 24 | ); 25 | 26 | const predOrig = isObjectOf({ 27 | a: (x: unknown): x is string => typeof x === "string", 28 | b: (x: unknown): x is number => typeof x === "number", 29 | }); 30 | const pred420 = isReadonlyOf420(predOrig); 31 | const pred = isReadonlyOf(predOrig); 32 | 33 | Deno.bench({ 34 | name: "current", 35 | fn() { 36 | assert(repeats.every(() => pred(positive))); 37 | }, 38 | group: "isReadonlyOf (positive)", 39 | }); 40 | 41 | Deno.bench({ 42 | name: "current", 43 | fn() { 44 | assert(repeats.every(() => pred(positive10))); 45 | }, 46 | group: "isReadonlyOf (positive 10)", 47 | }); 48 | 49 | Deno.bench({ 50 | name: "current", 51 | fn() { 52 | assert(repeats.every(() => pred(positive1000))); 53 | }, 54 | group: "isReadonlyOf (positive 1000)", 55 | }); 56 | 57 | Deno.bench({ 58 | name: "current", 59 | fn() { 60 | assert(repeats.every(() => !pred(negative))); 61 | }, 62 | group: "isReadonlyOf (negative)", 63 | }); 64 | 65 | Deno.bench({ 66 | name: "current", 67 | fn() { 68 | assert(repeats.every(() => !pred(negative10))); 69 | }, 70 | group: "isReadonlyOf (negative 10)", 71 | }); 72 | 73 | Deno.bench({ 74 | name: "current", 75 | fn() { 76 | assert(repeats.every(() => !pred(negative1000))); 77 | }, 78 | group: "isReadonlyOf (negative 1000)", 79 | }); 80 | 81 | Deno.bench({ 82 | name: "v4.2.0", 83 | fn() { 84 | assert(repeats.every(() => pred420(positive))); 85 | }, 86 | group: "isReadonlyOf (positive)", 87 | }); 88 | 89 | Deno.bench({ 90 | name: "v4.2.0", 91 | fn() { 92 | assert(repeats.every(() => pred420(positive10))); 93 | }, 94 | group: "isReadonlyOf (positive 10)", 95 | }); 96 | 97 | Deno.bench({ 98 | name: "v4.2.0", 99 | fn() { 100 | assert(repeats.every(() => pred420(positive1000))); 101 | }, 102 | group: "isReadonlyOf (positive 1000)", 103 | }); 104 | 105 | Deno.bench({ 106 | name: "v4.2.0", 107 | fn() { 108 | assert(repeats.every(() => !pred420(negative))); 109 | }, 110 | group: "isReadonlyOf (negative)", 111 | }); 112 | 113 | Deno.bench({ 114 | name: "v4.2.0", 115 | fn() { 116 | assert(repeats.every(() => !pred420(negative10))); 117 | }, 118 | group: "isReadonlyOf (negative 10)", 119 | }); 120 | 121 | Deno.bench({ 122 | name: "v4.2.0", 123 | fn() { 124 | assert(repeats.every(() => !pred420(negative1000))); 125 | }, 126 | group: "isReadonlyOf (negative 1000)", 127 | }); 128 | -------------------------------------------------------------------------------- /is/record.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Return `true` if the type of `x` satisfies `Record`. 3 | * 4 | * Note that this function returns `true` for ambiguous instances like `Set`, `Map`, `Date`, `Promise`, etc. 5 | * Use {@linkcode [is/record-object].isRecordObject|isRecordObject} instead if you want to check if `x` is an instance of `Object`. 6 | * 7 | * ```ts 8 | * import { is } from "@core/unknownutil"; 9 | * 10 | * const a: unknown = {"a": 0, "b": 1}; 11 | * if (is.Record(a)) { 12 | * const _: Record = a; 13 | * } 14 | * 15 | * const b: unknown = new Set(); 16 | * if (is.Record(b)) { 17 | * const _: Record = b; 18 | * } 19 | * ``` 20 | */ 21 | export function isRecord( 22 | x: unknown, 23 | ): x is Record { 24 | return x != null && !Array.isArray(x) && typeof x === "object"; 25 | } 26 | -------------------------------------------------------------------------------- /is/record_bench.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "@std/assert"; 2 | import { isRecord as isRecord420 } from "jsr:@core/unknownutil@4.2.0/is/record"; 3 | import { isRecord } from "./record.ts"; 4 | 5 | const repeats = Array.from({ length: 100 }); 6 | const positive: unknown = {}; 7 | const negative: unknown = 0; 8 | 9 | Deno.bench({ 10 | name: "current", 11 | fn() { 12 | assert(repeats.every(() => isRecord(positive))); 13 | }, 14 | group: "isRecord (positive)", 15 | }); 16 | 17 | Deno.bench({ 18 | name: "current", 19 | fn() { 20 | assert(repeats.every(() => !isRecord(negative))); 21 | }, 22 | group: "isRecord (negative)", 23 | }); 24 | 25 | Deno.bench({ 26 | name: "v4.2.0", 27 | fn() { 28 | assert(repeats.every(() => isRecord420(positive))); 29 | }, 30 | group: "isRecord (positive)", 31 | }); 32 | 33 | Deno.bench({ 34 | name: "v4.2.0", 35 | fn() { 36 | assert(repeats.every(() => !isRecord420(negative))); 37 | }, 38 | group: "isRecord (negative)", 39 | }); 40 | -------------------------------------------------------------------------------- /is/record_object.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Return `true` if the type of `x` is an object instance that satisfies `Record`. 3 | * 4 | * Note that this function check if the `x` is an instance of `Object`. 5 | * Use {@linkcode [is/record].isRecord|isRecord} instead if you want to check if the `x` satisfies the `Record` type. 6 | * 7 | * ```ts 8 | * import { is } from "@core/unknownutil"; 9 | * 10 | * const a: unknown = {"a": 0, "b": 1}; 11 | * if (is.RecordObject(a)) { 12 | * const _: Record = a; 13 | * } 14 | * 15 | * const b: unknown = new Set(); 16 | * if (is.RecordObject(b)) { 17 | * // b is not a raw object, so it is not narrowed 18 | * } 19 | * ``` 20 | */ 21 | export function isRecordObject( 22 | x: unknown, 23 | ): x is Record { 24 | return x != null && typeof x === "object" && x.constructor === Object; 25 | } 26 | -------------------------------------------------------------------------------- /is/record_object_bench.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "@std/assert"; 2 | import { isRecordObject as isRecordObject420 } from "jsr:@core/unknownutil@4.2.0/is/record-object"; 3 | import { isRecordObject } from "./record_object.ts"; 4 | 5 | const repeats = Array.from({ length: 100 }); 6 | const positive: unknown = {}; 7 | const negative: unknown = new Map(); 8 | 9 | Deno.bench({ 10 | name: "current", 11 | fn() { 12 | assert(repeats.every(() => isRecordObject(positive))); 13 | }, 14 | group: "isRecordObject (positive)", 15 | }); 16 | 17 | Deno.bench({ 18 | name: "current", 19 | fn() { 20 | assert(repeats.every(() => !isRecordObject(negative))); 21 | }, 22 | group: "isRecordObject (negative)", 23 | }); 24 | 25 | Deno.bench({ 26 | name: "v4.2.0", 27 | fn() { 28 | assert(repeats.every(() => isRecordObject420(positive))); 29 | }, 30 | group: "isRecordObject (positive)", 31 | }); 32 | 33 | Deno.bench({ 34 | name: "v4.2.0", 35 | fn() { 36 | assert(repeats.every(() => !isRecordObject420(negative))); 37 | }, 38 | group: "isRecordObject (negative)", 39 | }); 40 | -------------------------------------------------------------------------------- /is/record_object_of.ts: -------------------------------------------------------------------------------- 1 | import { rewriteName } from "../_funcutil.ts"; 2 | import type { Predicate } from "../type.ts"; 3 | import { isRecordObject } from "./record_object.ts"; 4 | 5 | /** 6 | * Return a type predicate function that returns `true` if the type of `x` is an Object instance that satisfies `Record`. 7 | * 8 | * Note that this function check if the `x` is an instance of `Object`. 9 | * Use {@linkcode [is/record-of].isRecordOf|isRecordOf} instead if you want to check if the `x` satisfies the `Record` type. 10 | * 11 | * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. 12 | * 13 | * ```ts 14 | * import { is } from "@core/unknownutil"; 15 | * 16 | * const isMyType = is.RecordObjectOf(is.Number); 17 | * const a: unknown = {"a": 0, "b": 1}; 18 | * if (isMyType(a)) { 19 | * const _: Record = a; 20 | * } 21 | * ``` 22 | * 23 | * With predicate function for keys: 24 | * 25 | * ```ts 26 | * import { is } from "@core/unknownutil"; 27 | * 28 | * const isMyType = is.RecordObjectOf(is.Number, is.String); 29 | * const a: unknown = {"a": 0, "b": 1}; 30 | * if (isMyType(a)) { 31 | * const _: Record = a; 32 | * } 33 | * ``` 34 | */ 35 | export function isRecordObjectOf( 36 | pred: Predicate, 37 | predKey?: Predicate, 38 | ): Predicate> { 39 | return rewriteName( 40 | (x: unknown): x is Record => { 41 | if (!isRecordObject(x)) return false; 42 | const keys = [ 43 | ...Object.keys(x), 44 | ...Object.getOwnPropertySymbols(x), 45 | ]; 46 | for (const k of keys) { 47 | if (!pred(x[k])) return false; 48 | if (predKey && !predKey(k)) return false; 49 | } 50 | return true; 51 | }, 52 | "isRecordObjectOf", 53 | pred, 54 | predKey, 55 | ); 56 | } 57 | -------------------------------------------------------------------------------- /is/record_object_test.ts: -------------------------------------------------------------------------------- 1 | import { testWithExamples } from "../_testutil.ts"; 2 | import { isRecordObject } from "./record_object.ts"; 3 | 4 | Deno.test("isRecordObject", async (t) => { 5 | await testWithExamples(t, isRecordObject, { 6 | validExamples: ["record"], 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /is/record_of.ts: -------------------------------------------------------------------------------- 1 | import { rewriteName } from "../_funcutil.ts"; 2 | import type { Predicate } from "../type.ts"; 3 | import { isRecord } from "./record.ts"; 4 | 5 | /** 6 | * Return a type predicate function that returns `true` if the type of `x` satisfies `Record`. 7 | * 8 | * Note that this function only check if the `x` satisfies the `Record` type. 9 | * Use {@linkcode [is/record-object-of].isRecordObjectOf|isRecordObjectOf} instead if you want to check if the `x` is an instance of `Object`. 10 | * 11 | * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. 12 | * 13 | * ```ts 14 | * import { is } from "@core/unknownutil"; 15 | * 16 | * const isMyType = is.RecordOf(is.Number); 17 | * const a: unknown = {"a": 0, "b": 1}; 18 | * if (isMyType(a)) { 19 | * const _: Record = a; 20 | * } 21 | * ``` 22 | * 23 | * With predicate function for keys: 24 | * 25 | * ```ts 26 | * import { is } from "@core/unknownutil"; 27 | * 28 | * const isMyType = is.RecordOf(is.Number, is.String); 29 | * const a: unknown = {"a": 0, "b": 1}; 30 | * if (isMyType(a)) { 31 | * const _: Record = a; 32 | * } 33 | * ``` 34 | */ 35 | export function isRecordOf( 36 | pred: Predicate, 37 | predKey?: Predicate, 38 | ): Predicate> { 39 | return rewriteName( 40 | (x: unknown): x is Record => { 41 | if (!isRecord(x)) return false; 42 | const keys = [ 43 | ...Object.keys(x), 44 | ...Object.getOwnPropertySymbols(x), 45 | ]; 46 | for (const k of keys) { 47 | if (!pred(x[k])) return false; 48 | if (predKey && !predKey(k)) return false; 49 | } 50 | return true; 51 | }, 52 | "isRecordOf", 53 | pred, 54 | predKey, 55 | ); 56 | } 57 | -------------------------------------------------------------------------------- /is/record_test.ts: -------------------------------------------------------------------------------- 1 | import { testWithExamples } from "../_testutil.ts"; 2 | import { isRecord } from "./record.ts"; 3 | 4 | Deno.test("isRecord", async (t) => { 5 | await testWithExamples(t, isRecord, { 6 | validExamples: ["record", "date", "promise", "set", "map"], 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /is/required_of.ts: -------------------------------------------------------------------------------- 1 | import type { FlatType } from "../_typeutil.ts"; 2 | import type { IsPredObj } from "../_annotation.ts"; 3 | import { asUnoptional } from "../as/optional.ts"; 4 | import type { Predicate } from "../type.ts"; 5 | import { isObjectOf } from "./object_of.ts"; 6 | 7 | /** 8 | * Return a type predicate function that returns `true` if the type of `x` is `Required>`. 9 | * 10 | * It only supports modifing a predicate function annotated with `IsPredObj`, usually returned by the followings 11 | * 12 | * - {@linkcode [is/intersection-of].isIntersectionOf|isIntersectionOf} 13 | * - {@linkcode [is/object-of].isObjectOf|isObjectOf} 14 | * - {@linkcode [is/omit-of].isOmitOf|isOmitOf} 15 | * - {@linkcode [is/partial-of].isPartialOf|isPartialOf} 16 | * - {@linkcode [is/pick-of].isPickOf|isPickOf} 17 | * - {@linkcode [is/readonly-of].isReadonlyOf|isReadonlyOf} 18 | * - {@linkcode [is/required-of].isRequiredOf|isRequiredOf} 19 | * - {@linkcode [is/strict-of].isStrictOf|isStrictOf} 20 | * 21 | * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. 22 | * 23 | * ```typescript 24 | * import { as, is } from "@core/unknownutil"; 25 | * 26 | * const isMyType = is.RequiredOf(is.ObjectOf({ 27 | * a: is.Number, 28 | * b: is.UnionOf([is.String, is.Undefined]), 29 | * c: as.Optional(is.Boolean), 30 | * })); 31 | * const a: unknown = { a: 0, b: "b", c: true, other: "other" }; 32 | * if (isMyType(a)) { 33 | * const _: { a: number; b: string | undefined; c: boolean | undefined } = a; 34 | * } 35 | * ``` 36 | */ 37 | export function isRequiredOf< 38 | T extends Record, 39 | P extends Record>, 40 | >( 41 | pred: Predicate & IsPredObj

, 42 | ): 43 | & Predicate>> 44 | & IsPredObj

{ 45 | const keys = [ 46 | ...Object.keys(pred.predObj), 47 | ...Object.getOwnPropertySymbols(pred.predObj), 48 | ]; 49 | const predObj: Record> = { ...pred.predObj }; 50 | for (const key of keys) { 51 | predObj[key] = asUnoptional(predObj[key]); 52 | } 53 | return isObjectOf(predObj) as 54 | & Predicate>> 55 | & IsPredObj

; 56 | } 57 | -------------------------------------------------------------------------------- /is/required_of_bench.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "@std/assert"; 2 | import { isObjectOf } from "./object_of.ts"; 3 | import { isRequiredOf as isRequiredOf420 } from "jsr:@core/unknownutil@4.2.0/is/required-of"; 4 | import { isRequiredOf } from "./required_of.ts"; 5 | 6 | const repeats = Array.from({ length: 100 }); 7 | const positive: unknown = { a: "", b: 0 }; 8 | const positive10: unknown = Object.assign( 9 | { a: "", b: 0 }, 10 | Array.from({ length: 10 }), 11 | ); 12 | const positive1000: unknown = Object.assign( 13 | { a: "", b: 0 }, 14 | Array.from({ length: 1000 }), 15 | ); 16 | const negative: unknown = {}; 17 | const negative10: unknown = Object.assign( 18 | {}, 19 | Array.from({ length: 10 }), 20 | ); 21 | const negative1000: unknown = Object.assign( 22 | {}, 23 | Array.from({ length: 1000 }), 24 | ); 25 | 26 | const predOrig = isObjectOf({ 27 | a: (x: unknown): x is string => typeof x === "string", 28 | b: (x: unknown): x is number => typeof x === "number", 29 | }); 30 | const pred420 = isRequiredOf420(predOrig); 31 | const pred = isRequiredOf(predOrig); 32 | 33 | Deno.bench({ 34 | name: "current", 35 | fn() { 36 | assert(repeats.every(() => pred(positive))); 37 | }, 38 | group: "isRequiredOf (positive)", 39 | }); 40 | 41 | Deno.bench({ 42 | name: "current", 43 | fn() { 44 | assert(repeats.every(() => pred(positive10))); 45 | }, 46 | group: "isRequiredOf (positive 10)", 47 | }); 48 | 49 | Deno.bench({ 50 | name: "current", 51 | fn() { 52 | assert(repeats.every(() => pred(positive1000))); 53 | }, 54 | group: "isRequiredOf (positive 1000)", 55 | }); 56 | 57 | Deno.bench({ 58 | name: "current", 59 | fn() { 60 | assert(repeats.every(() => !pred(negative))); 61 | }, 62 | group: "isRequiredOf (negative)", 63 | }); 64 | 65 | Deno.bench({ 66 | name: "current", 67 | fn() { 68 | assert(repeats.every(() => !pred(negative10))); 69 | }, 70 | group: "isRequiredOf (negative 10)", 71 | }); 72 | 73 | Deno.bench({ 74 | name: "current", 75 | fn() { 76 | assert(repeats.every(() => !pred(negative1000))); 77 | }, 78 | group: "isRequiredOf (negative 1000)", 79 | }); 80 | 81 | Deno.bench({ 82 | name: "v4.2.0", 83 | fn() { 84 | assert(repeats.every(() => pred420(positive))); 85 | }, 86 | group: "isRequiredOf (positive)", 87 | }); 88 | 89 | Deno.bench({ 90 | name: "v4.2.0", 91 | fn() { 92 | assert(repeats.every(() => pred420(positive10))); 93 | }, 94 | group: "isRequiredOf (positive 10)", 95 | }); 96 | 97 | Deno.bench({ 98 | name: "v4.2.0", 99 | fn() { 100 | assert(repeats.every(() => pred420(positive1000))); 101 | }, 102 | group: "isRequiredOf (positive 1000)", 103 | }); 104 | 105 | Deno.bench({ 106 | name: "v4.2.0", 107 | fn() { 108 | assert(repeats.every(() => !pred420(negative))); 109 | }, 110 | group: "isRequiredOf (negative)", 111 | }); 112 | 113 | Deno.bench({ 114 | name: "v4.2.0", 115 | fn() { 116 | assert(repeats.every(() => !pred420(negative10))); 117 | }, 118 | group: "isRequiredOf (negative 10)", 119 | }); 120 | 121 | Deno.bench({ 122 | name: "v4.2.0", 123 | fn() { 124 | assert(repeats.every(() => !pred420(negative1000))); 125 | }, 126 | group: "isRequiredOf (negative 1000)", 127 | }); 128 | -------------------------------------------------------------------------------- /is/set.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Return `true` if the type of `x` is `Set`. 3 | * 4 | * Use {@linkcode [is/set-of].isSetOf|isSetOf} to check if the type of `x` is a set of `T`. 5 | * 6 | * ```ts 7 | * import { is } from "@core/unknownutil"; 8 | * 9 | * const a: unknown = new Set([0, 1, 2]); 10 | * if (is.Set(a)) { 11 | * const _: Set = a; 12 | * } 13 | * ``` 14 | */ 15 | export function isSet(x: unknown): x is Set { 16 | return x instanceof Set; 17 | } 18 | -------------------------------------------------------------------------------- /is/set_bench.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "@std/assert"; 2 | import { isSet as isSet420 } from "jsr:@core/unknownutil@4.2.0/is/set"; 3 | import { isSet } from "./set.ts"; 4 | 5 | const repeats = Array.from({ length: 100 }); 6 | const positive: unknown = new Set(); 7 | const negative: unknown = []; 8 | 9 | Deno.bench({ 10 | name: "current", 11 | fn() { 12 | assert(repeats.every(() => isSet(positive))); 13 | }, 14 | group: "isSet (positive)", 15 | }); 16 | 17 | Deno.bench({ 18 | name: "current", 19 | fn() { 20 | assert(repeats.every(() => !isSet(negative))); 21 | }, 22 | group: "isSet (negative)", 23 | }); 24 | 25 | Deno.bench({ 26 | name: "v4.2.0", 27 | fn() { 28 | assert(repeats.every(() => isSet420(positive))); 29 | }, 30 | group: "isSet (positive)", 31 | }); 32 | 33 | Deno.bench({ 34 | name: "v4.2.0", 35 | fn() { 36 | assert(repeats.every(() => !isSet420(negative))); 37 | }, 38 | group: "isSet (negative)", 39 | }); 40 | -------------------------------------------------------------------------------- /is/set_of.ts: -------------------------------------------------------------------------------- 1 | import { rewriteName } from "../_funcutil.ts"; 2 | import type { Predicate } from "../type.ts"; 3 | import { isSet } from "./set.ts"; 4 | 5 | /** 6 | * Return a type predicate function that returns `true` if the type of `x` is `Set`. 7 | * 8 | * Use {@linkcode [is/set].isSet|isSet} to check if the type of `x` is a set of `unknown`. 9 | * 10 | * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. 11 | * 12 | * ```ts 13 | * import { is } from "@core/unknownutil"; 14 | * 15 | * const isMyType = is.SetOf(is.String); 16 | * const a: unknown = new Set(["a", "b", "c"]); 17 | * if (isMyType(a)) { 18 | * const _: Set = a; 19 | * } 20 | * ``` 21 | */ 22 | export function isSetOf( 23 | pred: Predicate, 24 | ): Predicate> { 25 | return rewriteName( 26 | (x: unknown): x is Set => { 27 | if (!isSet(x)) return false; 28 | for (const v of x.values()) { 29 | if (!pred(v)) return false; 30 | } 31 | return true; 32 | }, 33 | "isSetOf", 34 | pred, 35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /is/set_of_bench.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "@std/assert"; 2 | import { isSetOf as isSetOf420 } from "jsr:@core/unknownutil@4.2.0/is/set-of"; 3 | import { isSetOf } from "./set_of.ts"; 4 | 5 | const repeats = Array.from({ length: 100 }); 6 | const positive: unknown = new Set(); 7 | const positive10: unknown = new Set( 8 | Array.from({ length: 10 }).map((_, i) => i.toString()), 9 | ); 10 | const positive1000: unknown = new Set( 11 | Array.from({ length: 1000 }).map((_, i) => i.toString()), 12 | ); 13 | const negative: unknown = { "": 0 }; 14 | const negative10: unknown = new Set( 15 | Array.from({ length: 10 }).map((_, i) => i), 16 | ); 17 | const negative1000: unknown = new Set( 18 | Array.from({ length: 1000 }).map((_, i) => i), 19 | ); 20 | 21 | const pred420 = isSetOf420((x: unknown): x is string => typeof x === "string"); 22 | const pred = isSetOf((x: unknown): x is string => typeof x === "string"); 23 | 24 | Deno.bench({ 25 | name: "current", 26 | fn() { 27 | assert(repeats.every(() => pred(positive))); 28 | }, 29 | group: "isSetOf (positive)", 30 | }); 31 | 32 | Deno.bench({ 33 | name: "current", 34 | fn() { 35 | assert(repeats.every(() => pred(positive10))); 36 | }, 37 | group: "isSetOf (positive 10)", 38 | }); 39 | 40 | Deno.bench({ 41 | name: "current", 42 | fn() { 43 | assert(repeats.every(() => pred(positive1000))); 44 | }, 45 | group: "isSetOf (positive 1000)", 46 | }); 47 | 48 | Deno.bench({ 49 | name: "current", 50 | fn() { 51 | assert(repeats.every(() => !pred(negative))); 52 | }, 53 | group: "isSetOf (negative)", 54 | }); 55 | 56 | Deno.bench({ 57 | name: "current", 58 | fn() { 59 | assert(repeats.every(() => !pred(negative10))); 60 | }, 61 | group: "isSetOf (negative 10)", 62 | }); 63 | 64 | Deno.bench({ 65 | name: "current", 66 | fn() { 67 | assert(repeats.every(() => !pred(negative1000))); 68 | }, 69 | group: "isSetOf (negative 1000)", 70 | }); 71 | 72 | Deno.bench({ 73 | name: "v4.2.0", 74 | fn() { 75 | assert(repeats.every(() => pred420(positive))); 76 | }, 77 | group: "isSetOf (positive)", 78 | }); 79 | 80 | Deno.bench({ 81 | name: "v4.2.0", 82 | fn() { 83 | assert(repeats.every(() => pred420(positive10))); 84 | }, 85 | group: "isSetOf (positive 10)", 86 | }); 87 | 88 | Deno.bench({ 89 | name: "v4.2.0", 90 | fn() { 91 | assert(repeats.every(() => pred420(positive1000))); 92 | }, 93 | group: "isSetOf (positive 1000)", 94 | }); 95 | 96 | Deno.bench({ 97 | name: "v4.2.0", 98 | fn() { 99 | assert(repeats.every(() => !pred420(negative))); 100 | }, 101 | group: "isSetOf (negative)", 102 | }); 103 | 104 | Deno.bench({ 105 | name: "v4.2.0", 106 | fn() { 107 | assert(repeats.every(() => !pred420(negative10))); 108 | }, 109 | group: "isSetOf (negative 10)", 110 | }); 111 | 112 | Deno.bench({ 113 | name: "v4.2.0", 114 | fn() { 115 | assert(repeats.every(() => !pred420(negative1000))); 116 | }, 117 | group: "isSetOf (negative 1000)", 118 | }); 119 | -------------------------------------------------------------------------------- /is/set_of_test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from "@std/assert"; 2 | import { assertSnapshot } from "@std/testing/snapshot"; 3 | import { assertType, type IsExact } from "@std/testing/types"; 4 | import { is } from "./mod.ts"; 5 | import { isSetOf } from "./set_of.ts"; 6 | 7 | Deno.test("isSetOf", async (t) => { 8 | await t.step("returns properly named predicate function", async (t) => { 9 | await assertSnapshot(t, isSetOf(is.Number).name); 10 | await assertSnapshot(t, isSetOf((_x): _x is string => false).name); 11 | }); 12 | 13 | await t.step("returns true on T set", () => { 14 | assertEquals(isSetOf(is.Number)(new Set([0, 1, 2])), true); 15 | assertEquals(isSetOf(is.String)(new Set(["a", "b", "c"])), true); 16 | assertEquals(isSetOf(is.Boolean)(new Set([true, false, true])), true); 17 | }); 18 | 19 | await t.step("returns false on non T set", () => { 20 | assertEquals(isSetOf(is.String)("a"), false, "Not a Set"); 21 | assertEquals(isSetOf(is.String)(new Set([0, 1, 2])), false); 22 | assertEquals(isSetOf(is.Number)(new Set(["a", "b", "c"])), false); 23 | assertEquals(isSetOf(is.String)(new Set([true, false, true])), false); 24 | }); 25 | 26 | await t.step("predicated type is correct", () => { 27 | const a: unknown = new Set([0, 1, 2]); 28 | if (isSetOf(is.Number)(a)) { 29 | assertType>>(true); 30 | } 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /is/set_test.ts: -------------------------------------------------------------------------------- 1 | import { testWithExamples } from "../_testutil.ts"; 2 | import { isSet } from "./set.ts"; 3 | 4 | Deno.test("isSet", async (t) => { 5 | await testWithExamples(t, isSet, { validExamples: ["set"] }); 6 | }); 7 | -------------------------------------------------------------------------------- /is/strict_of.ts: -------------------------------------------------------------------------------- 1 | import { rewriteName } from "../_funcutil.ts"; 2 | import type { IsPredObj } from "../_annotation.ts"; 3 | import type { Predicate } from "../type.ts"; 4 | 5 | /** 6 | * Return a type predicate function that returns `true` if the type of `x` is strictly follow the `ObjectOf`. 7 | * 8 | * It only supports modifing a predicate function annotated with `IsPredObj`, usually returned by the followings 9 | * 10 | * - {@linkcode [is/intersection-of].isIntersectionOf|isIntersectionOf} 11 | * - {@linkcode [is/object-of].isObjectOf|isObjectOf} 12 | * - {@linkcode [is/omit-of].isOmitOf|isOmitOf} 13 | * - {@linkcode [is/partial-of].isPartialOf|isPartialOf} 14 | * - {@linkcode [is/pick-of].isPickOf|isPickOf} 15 | * - {@linkcode [is/readonly-of].isReadonlyOf|isReadonlyOf} 16 | * - {@linkcode [is/required-of].isRequiredOf|isRequiredOf} 17 | * - {@linkcode [is/strict-of].isStrictOf|isStrictOf} 18 | * 19 | * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. 20 | * 21 | * ```ts 22 | * import { as, is } from "@core/unknownutil"; 23 | * 24 | * const isMyType = is.StrictOf(is.ObjectOf({ 25 | * a: is.Number, 26 | * b: is.String, 27 | * c: as.Optional(is.Boolean), 28 | * })); 29 | * const a: unknown = { a: 0, b: "a", other: "other" }; 30 | * if (isMyType(a)) { 31 | * // This block will not be executed because of "other" key in `a`. 32 | * } 33 | * ``` 34 | */ 35 | export function isStrictOf< 36 | T extends Record, 37 | P extends Record>, 38 | >( 39 | pred: Predicate & IsPredObj

, 40 | ): 41 | & Predicate 42 | & IsPredObj

{ 43 | const s = new Set(getKeys(pred.predObj)); 44 | return rewriteName( 45 | (x: unknown): x is T => { 46 | if (!pred(x)) return false; 47 | const ks = new Set(getKeys(x)); 48 | return ks.difference(s).size === 0; 49 | }, 50 | "isStrictOf", 51 | pred, 52 | ) as Predicate & IsPredObj

; 53 | } 54 | 55 | function getKeys(o: Record) { 56 | return [...Object.keys(o), ...Object.getOwnPropertySymbols(o)]; 57 | } 58 | -------------------------------------------------------------------------------- /is/strict_of_bench.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "@std/assert"; 2 | import { isObjectOf } from "./object_of.ts"; 3 | import { isStrictOf as isStrictOf420 } from "jsr:@core/unknownutil@4.2.0/is/strict-of"; 4 | import { isStrictOf } from "./strict_of.ts"; 5 | 6 | const repeats = Array.from({ length: 100 }); 7 | const positive: unknown = { a: "", b: 0 }; 8 | const negative: unknown = { a: "", b: 0, c: true }; 9 | 10 | const predOrig = isObjectOf({ 11 | a: (x: unknown): x is string => typeof x === "string", 12 | b: (x: unknown): x is number => typeof x === "number", 13 | }); 14 | const pred420 = isStrictOf420(predOrig); 15 | const pred = isStrictOf(predOrig); 16 | 17 | Deno.bench({ 18 | name: "current", 19 | fn() { 20 | assert(repeats.every(() => pred(positive))); 21 | }, 22 | group: "isStrictOf (positive)", 23 | }); 24 | 25 | Deno.bench({ 26 | name: "current", 27 | fn() { 28 | assert(repeats.every(() => !pred(negative))); 29 | }, 30 | group: "isStrictOf (negative)", 31 | }); 32 | 33 | Deno.bench({ 34 | name: "v4.2.0", 35 | fn() { 36 | assert(repeats.every(() => pred420(positive))); 37 | }, 38 | group: "isStrictOf (positive)", 39 | }); 40 | 41 | Deno.bench({ 42 | name: "v4.2.0", 43 | fn() { 44 | assert(repeats.every(() => !pred420(negative))); 45 | }, 46 | group: "isStrictOf (negative)", 47 | }); 48 | -------------------------------------------------------------------------------- /is/string.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Return `true` if the type of `x` is `string`. 3 | * 4 | * ```ts 5 | * import { is } from "@core/unknownutil"; 6 | * 7 | * const a: unknown = "a"; 8 | * if (is.String(a)) { 9 | * const _: string = a; 10 | * } 11 | * ``` 12 | */ 13 | export function isString(x: unknown): x is string { 14 | return typeof x === "string"; 15 | } 16 | -------------------------------------------------------------------------------- /is/string_bench.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "@std/assert"; 2 | import { isString as isString420 } from "jsr:@core/unknownutil@4.2.0/is/string"; 3 | import { isString } from "./string.ts"; 4 | 5 | const repeats = Array.from({ length: 100 }); 6 | const positive: unknown = ""; 7 | const negative: unknown = 0; 8 | 9 | Deno.bench({ 10 | name: "current", 11 | fn() { 12 | assert(repeats.every(() => isString(positive))); 13 | }, 14 | group: "isString (positive)", 15 | }); 16 | 17 | Deno.bench({ 18 | name: "current", 19 | fn() { 20 | assert(repeats.every(() => !isString(negative))); 21 | }, 22 | group: "isString (negative)", 23 | }); 24 | 25 | Deno.bench({ 26 | name: "v4.2.0", 27 | fn() { 28 | assert(repeats.every(() => isString420(positive))); 29 | }, 30 | group: "isString (positive)", 31 | }); 32 | 33 | Deno.bench({ 34 | name: "v4.2.0", 35 | fn() { 36 | assert(repeats.every(() => !isString420(negative))); 37 | }, 38 | group: "isString (negative)", 39 | }); 40 | -------------------------------------------------------------------------------- /is/string_test.ts: -------------------------------------------------------------------------------- 1 | import { testWithExamples } from "../_testutil.ts"; 2 | import { isString } from "./string.ts"; 3 | 4 | Deno.test("isString", async (t) => { 5 | await testWithExamples(t, isString, { validExamples: ["string"] }); 6 | }); 7 | -------------------------------------------------------------------------------- /is/symbol.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Return `true` if the type of `x` is `symbol`. 3 | * 4 | * ```ts 5 | * import { is } from "@core/unknownutil"; 6 | * 7 | * const a: unknown = Symbol("symbol"); 8 | * if (is.Symbol(a)) { 9 | * const _: symbol = a; 10 | * } 11 | * ``` 12 | */ 13 | export function isSymbol(x: unknown): x is symbol { 14 | return typeof x === "symbol"; 15 | } 16 | -------------------------------------------------------------------------------- /is/symbol_bench.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "@std/assert"; 2 | import { isSymbol as isSymbol420 } from "jsr:@core/unknownutil@4.2.0/is/symbol"; 3 | import { isSymbol } from "./symbol.ts"; 4 | 5 | const repeats = Array.from({ length: 100 }); 6 | const positive: unknown = Symbol(); 7 | const negative: unknown = 0; 8 | 9 | Deno.bench({ 10 | name: "current", 11 | fn() { 12 | assert(repeats.every(() => isSymbol(positive))); 13 | }, 14 | group: "isSymbol (positive)", 15 | }); 16 | 17 | Deno.bench({ 18 | name: "current", 19 | fn() { 20 | assert(repeats.every(() => !isSymbol(negative))); 21 | }, 22 | group: "isSymbol (negative)", 23 | }); 24 | 25 | Deno.bench({ 26 | name: "v4.2.0", 27 | fn() { 28 | assert(repeats.every(() => isSymbol420(positive))); 29 | }, 30 | group: "isSymbol (positive)", 31 | }); 32 | 33 | Deno.bench({ 34 | name: "v4.2.0", 35 | fn() { 36 | assert(repeats.every(() => !isSymbol420(negative))); 37 | }, 38 | group: "isSymbol (negative)", 39 | }); 40 | -------------------------------------------------------------------------------- /is/symbol_test.ts: -------------------------------------------------------------------------------- 1 | import { testWithExamples } from "../_testutil.ts"; 2 | import { isSymbol } from "./symbol.ts"; 3 | 4 | Deno.test("isSymbol", async (t) => { 5 | await testWithExamples(t, isSymbol, { validExamples: ["symbol"] }); 6 | }); 7 | -------------------------------------------------------------------------------- /is/sync_function.ts: -------------------------------------------------------------------------------- 1 | const objectToString = Object.prototype.toString; 2 | 3 | /** 4 | * Return `true` if the type of `x` is `function` (non async function). 5 | * 6 | * Use {@linkcode [is/function].isFunction|isFunction} to check if the type of `x` is a function. 7 | * Use {@linkcode [is/async-function].isAsyncFunction|isAsyncFunction} to check if the type of `x` is an asynchronous function. 8 | * 9 | * ```ts 10 | * import { is } from "@core/unknownutil"; 11 | * 12 | * const a: unknown = () => {}; 13 | * if (is.SyncFunction(a)) { 14 | * const _: ((...args: unknown[]) => unknown) = a; 15 | * } 16 | * ``` 17 | */ 18 | export function isSyncFunction( 19 | x: unknown, 20 | ): x is (...args: unknown[]) => unknown { 21 | return objectToString.call(x) === "[object Function]"; 22 | } 23 | -------------------------------------------------------------------------------- /is/sync_function_bench.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "@std/assert"; 2 | import { isSyncFunction as isSyncFunction420 } from "jsr:@core/unknownutil@4.2.0/is/sync-function"; 3 | import { isSyncFunction } from "./sync_function.ts"; 4 | 5 | const repeats = Array.from({ length: 100 }); 6 | const positive: unknown = () => {}; 7 | const negative: unknown = async () => {}; 8 | 9 | Deno.bench({ 10 | name: "current", 11 | fn() { 12 | assert(repeats.every(() => isSyncFunction(positive))); 13 | }, 14 | group: "isSyncFunction (positive)", 15 | }); 16 | 17 | Deno.bench({ 18 | name: "current", 19 | fn() { 20 | assert(repeats.every(() => !isSyncFunction(negative))); 21 | }, 22 | group: "isSyncFunction (negative)", 23 | }); 24 | 25 | Deno.bench({ 26 | name: "v4.2.0", 27 | fn() { 28 | assert(repeats.every(() => isSyncFunction420(positive))); 29 | }, 30 | group: "isSyncFunction (positive)", 31 | }); 32 | 33 | Deno.bench({ 34 | name: "v4.2.0", 35 | fn() { 36 | assert(repeats.every(() => !isSyncFunction420(negative))); 37 | }, 38 | group: "isSyncFunction (negative)", 39 | }); 40 | -------------------------------------------------------------------------------- /is/sync_function_test.ts: -------------------------------------------------------------------------------- 1 | import { testWithExamples } from "../_testutil.ts"; 2 | import { isSyncFunction } from "./sync_function.ts"; 3 | 4 | Deno.test("isSyncFunction", async (t) => { 5 | await testWithExamples(t, isSyncFunction, { 6 | validExamples: ["syncFunction"], 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /is/tuple_of_bench.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "@std/assert"; 2 | import { isTupleOf as isTupleOf420 } from "jsr:@core/unknownutil@4.2.0/is/tuple-of"; 3 | import { isTupleOf } from "./tuple_of.ts"; 4 | 5 | const repeats = Array.from({ length: 100 }); 6 | const positive: unknown = ["", 0]; 7 | const positive10: unknown = ["", 0, ...Array.from({ length: 10 })]; 8 | const positive1000: unknown = ["", 0, ...Array.from({ length: 1000 })]; 9 | const negative: unknown = []; 10 | const negative10: unknown = Array.from({ length: 10 }); 11 | const negative1000: unknown = Array.from({ length: 1000 }); 12 | 13 | const predTup = [ 14 | (x: unknown): x is string => typeof x === "string", 15 | (x: unknown): x is number => typeof x === "number", 16 | ] as const; 17 | const predElse = (_x: unknown): _x is number[] => true; 18 | const pred420 = isTupleOf420(predTup, predElse); 19 | const pred = isTupleOf(predTup, predElse); 20 | 21 | Deno.bench({ 22 | name: "current", 23 | fn() { 24 | assert(repeats.every(() => pred(positive))); 25 | }, 26 | group: "isTupleOf (positive)", 27 | }); 28 | 29 | Deno.bench({ 30 | name: "current", 31 | fn() { 32 | assert(repeats.every(() => pred(positive10))); 33 | }, 34 | group: "isTupleOf (positive 10)", 35 | }); 36 | 37 | Deno.bench({ 38 | name: "current", 39 | fn() { 40 | assert(repeats.every(() => pred(positive1000))); 41 | }, 42 | group: "isTupleOf (positive 1000)", 43 | }); 44 | 45 | Deno.bench({ 46 | name: "current", 47 | fn() { 48 | assert(repeats.every(() => !pred(negative))); 49 | }, 50 | group: "isTupleOf (negative)", 51 | }); 52 | 53 | Deno.bench({ 54 | name: "current", 55 | fn() { 56 | assert(repeats.every(() => !pred(negative10))); 57 | }, 58 | group: "isTupleOf (negative 10)", 59 | }); 60 | 61 | Deno.bench({ 62 | name: "current", 63 | fn() { 64 | assert(repeats.every(() => !pred(negative1000))); 65 | }, 66 | group: "isTupleOf (negative 1000)", 67 | }); 68 | 69 | Deno.bench({ 70 | name: "v4.2.0", 71 | fn() { 72 | assert(repeats.every(() => pred420(positive))); 73 | }, 74 | group: "isTupleOf (positive)", 75 | }); 76 | 77 | Deno.bench({ 78 | name: "v4.2.0", 79 | fn() { 80 | assert(repeats.every(() => pred420(positive10))); 81 | }, 82 | group: "isTupleOf (positive 10)", 83 | }); 84 | 85 | Deno.bench({ 86 | name: "v4.2.0", 87 | fn() { 88 | assert(repeats.every(() => pred420(positive1000))); 89 | }, 90 | group: "isTupleOf (positive 1000)", 91 | }); 92 | 93 | Deno.bench({ 94 | name: "v4.2.0", 95 | fn() { 96 | assert(repeats.every(() => !pred420(negative))); 97 | }, 98 | group: "isTupleOf (negative)", 99 | }); 100 | 101 | Deno.bench({ 102 | name: "v4.2.0", 103 | fn() { 104 | assert(repeats.every(() => !pred420(negative10))); 105 | }, 106 | group: "isTupleOf (negative 10)", 107 | }); 108 | 109 | Deno.bench({ 110 | name: "v4.2.0", 111 | fn() { 112 | assert(repeats.every(() => !pred420(negative1000))); 113 | }, 114 | group: "isTupleOf (negative 1000)", 115 | }); 116 | -------------------------------------------------------------------------------- /is/undefined.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Return `true` if the type of `x` is `undefined`. 3 | * 4 | * ```ts 5 | * import { is } from "@core/unknownutil"; 6 | * 7 | * const a: unknown = undefined; 8 | * if (is.Undefined(a)) { 9 | * const _: undefined = a; 10 | * } 11 | * ``` 12 | */ 13 | export function isUndefined(x: unknown): x is undefined { 14 | return typeof x === "undefined"; 15 | } 16 | -------------------------------------------------------------------------------- /is/undefined_bench.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "@std/assert"; 2 | import { isUndefined as isUndefined420 } from "jsr:@core/unknownutil@4.2.0/is/undefined"; 3 | import { isUndefined } from "./undefined.ts"; 4 | 5 | const repeats = Array.from({ length: 100 }); 6 | const positive: unknown = undefined; 7 | const negative: unknown = 0; 8 | 9 | Deno.bench({ 10 | name: "current", 11 | fn() { 12 | assert(repeats.every(() => isUndefined(positive))); 13 | }, 14 | group: "isUndefined (positive)", 15 | }); 16 | 17 | Deno.bench({ 18 | name: "current", 19 | fn() { 20 | assert(repeats.every(() => !isUndefined(negative))); 21 | }, 22 | group: "isUndefined (negative)", 23 | }); 24 | 25 | Deno.bench({ 26 | name: "v4.2.0", 27 | fn() { 28 | assert(repeats.every(() => isUndefined420(positive))); 29 | }, 30 | group: "isUndefined (positive)", 31 | }); 32 | 33 | Deno.bench({ 34 | name: "v4.2.0", 35 | fn() { 36 | assert(repeats.every(() => !isUndefined420(negative))); 37 | }, 38 | group: "isUndefined (negative)", 39 | }); 40 | -------------------------------------------------------------------------------- /is/undefined_test.ts: -------------------------------------------------------------------------------- 1 | import { testWithExamples } from "../_testutil.ts"; 2 | import { isUndefined } from "./undefined.ts"; 3 | 4 | Deno.test("isUndefined", async (t) => { 5 | await testWithExamples(t, isUndefined, { validExamples: ["undefined"] }); 6 | }); 7 | -------------------------------------------------------------------------------- /is/uniform_tuple_of.ts: -------------------------------------------------------------------------------- 1 | import { rewriteName } from "../_funcutil.ts"; 2 | import type { Predicate } from "../type.ts"; 3 | import { isArray } from "./array.ts"; 4 | 5 | /** 6 | * Return a type predicate function that returns `true` if the type of `x` is `UniformTupleOf`. 7 | * 8 | * Use {@linkcode [is/tuple-of].isTupleOf|isTupleOf} to check if the type of `x` is a tuple of `T`. 9 | * 10 | * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. 11 | * 12 | * ```ts 13 | * import { is } from "@core/unknownutil"; 14 | * 15 | * const isMyType = is.UniformTupleOf(5); 16 | * const a: unknown = [0, 1, 2, 3, 4]; 17 | * if (isMyType(a)) { 18 | * const _: [unknown, unknown, unknown, unknown, unknown] = a; 19 | * } 20 | * ``` 21 | * 22 | * With predicate function: 23 | * 24 | * ```ts 25 | * import { is } from "@core/unknownutil"; 26 | * 27 | * const isMyType = is.UniformTupleOf(5, is.Number); 28 | * const a: unknown = [0, 1, 2, 3, 4]; 29 | * if (isMyType(a)) { 30 | * const _: [number, number, number, number, number] = a; 31 | * } 32 | * ``` 33 | */ 34 | export function isUniformTupleOf( 35 | n: N, 36 | pred?: Predicate, 37 | ): Predicate> { 38 | return rewriteName( 39 | (x: unknown): x is UniformTupleOf => { 40 | if (!isArray(x) || x.length !== n) { 41 | return false; 42 | } 43 | return !pred || x.every((v) => pred(v)); 44 | }, 45 | "isUniformTupleOf", 46 | n, 47 | pred, 48 | ); 49 | } 50 | 51 | // https://stackoverflow.com/a/71700658/1273406 52 | type UniformTupleOf< 53 | T, 54 | N extends number, 55 | R extends readonly T[] = [], 56 | > = R["length"] extends N ? R : UniformTupleOf; 57 | -------------------------------------------------------------------------------- /is/uniform_tuple_of_bench.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "@std/assert"; 2 | import { isUniformTupleOf as isUniformTupleOf420 } from "jsr:@core/unknownutil@4.2.0/is/uniform-tuple-of"; 3 | import { isUniformTupleOf } from "./uniform_tuple_of.ts"; 4 | 5 | const repeats = Array.from({ length: 100 }); 6 | const positive: unknown = [0, 0, 0]; 7 | const negative: unknown = [0, 0, 0, 0]; 8 | 9 | const predElse = (_x: unknown): _x is number[] => true; 10 | const pred420 = isUniformTupleOf420(3, predElse); 11 | const pred = isUniformTupleOf(3, predElse); 12 | 13 | Deno.bench({ 14 | name: "current", 15 | fn() { 16 | assert(repeats.every(() => pred(positive))); 17 | }, 18 | group: "isUniformTupleOf (positive)", 19 | }); 20 | 21 | Deno.bench({ 22 | name: "current", 23 | fn() { 24 | assert(repeats.every(() => !pred(negative))); 25 | }, 26 | group: "isUniformTupleOf (negative)", 27 | }); 28 | 29 | Deno.bench({ 30 | name: "v4.2.0", 31 | fn() { 32 | assert(repeats.every(() => pred420(positive))); 33 | }, 34 | group: "isUniformTupleOf (positive)", 35 | }); 36 | 37 | Deno.bench({ 38 | name: "v4.2.0", 39 | fn() { 40 | assert(repeats.every(() => !pred420(negative))); 41 | }, 42 | group: "isUniformTupleOf (negative)", 43 | }); 44 | -------------------------------------------------------------------------------- /is/uniform_tuple_of_test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from "@std/assert"; 2 | import { assertSnapshot } from "@std/testing/snapshot"; 3 | import { assertType, type IsExact } from "@std/testing/types"; 4 | import { is } from "./mod.ts"; 5 | import { isUniformTupleOf } from "./uniform_tuple_of.ts"; 6 | 7 | Deno.test("isUniformTupleOf", async (t) => { 8 | await t.step("returns properly named predicate function", async (t) => { 9 | await assertSnapshot(t, isUniformTupleOf(3).name); 10 | await assertSnapshot(t, isUniformTupleOf(3, is.Number).name); 11 | await assertSnapshot( 12 | t, 13 | isUniformTupleOf(3, (_x): _x is string => false).name, 14 | ); 15 | }); 16 | 17 | await t.step("returns true on mono-typed T tuple", () => { 18 | assertEquals(isUniformTupleOf(3)([0, 1, 2]), true); 19 | assertEquals(isUniformTupleOf(3, is.Number)([0, 1, 2]), true); 20 | }); 21 | 22 | await t.step("returns false on non mono-typed T tuple", () => { 23 | assertEquals(isUniformTupleOf(4)([0, 1, 2]), false); 24 | assertEquals(isUniformTupleOf(4)([0, 1, 2, 3, 4]), false); 25 | assertEquals(isUniformTupleOf(3, is.Number)(["a", "b", "c"]), false); 26 | }); 27 | 28 | await t.step("predicated type is correct", () => { 29 | const a: unknown = [0, 1, 2, 3, 4]; 30 | if (isUniformTupleOf(5)(a)) { 31 | assertType< 32 | IsExact 33 | >(true); 34 | } 35 | 36 | if (isUniformTupleOf(5, is.Number)(a)) { 37 | assertType>( 38 | true, 39 | ); 40 | } 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /is/union_of.ts: -------------------------------------------------------------------------------- 1 | import { rewriteName } from "../_funcutil.ts"; 2 | import { annotate } from "../_annotation.ts"; 3 | import type { Predicate } from "../type.ts"; 4 | 5 | /** 6 | * Return a type predicate function that returns `true` if the type of `x` is `UnionOf`. 7 | * 8 | * Use {@linkcode [is/intersection-of].isIntersectionOf|isIntersectionOf} to check if the type of `x` is an intersection of `T`. 9 | * 10 | * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. 11 | * 12 | * ```ts 13 | * import { is } from "@core/unknownutil"; 14 | * 15 | * const isMyType = is.UnionOf([is.Number, is.String, is.Boolean]); 16 | * const a: unknown = 0; 17 | * if (isMyType(a)) { 18 | * const _: number | string | boolean = a; 19 | * } 20 | * ``` 21 | * 22 | * Depending on the version of TypeScript and how values are provided, it may be necessary to add `as const` to the array 23 | * used as `preds`. If a type error occurs, try adding `as const` as follows: 24 | * 25 | * ```ts 26 | * import { is } from "@core/unknownutil"; 27 | * 28 | * const preds = [is.Number, is.String, is.Boolean] as const; 29 | * const isMyType = is.UnionOf(preds); 30 | * const a: unknown = 0; 31 | * if (isMyType(a)) { 32 | * const _: number | string | boolean = a; 33 | * } 34 | * ``` 35 | */ 36 | export function isUnionOf< 37 | T extends readonly [Predicate, ...Predicate[]], 38 | >( 39 | preds: T, 40 | ): Predicate> { 41 | return annotate( 42 | rewriteName( 43 | (x: unknown): x is UnionOf => preds.some((pred) => pred(x)), 44 | "isUnionOf", 45 | preds, 46 | ), 47 | "union", 48 | preds, 49 | ); 50 | } 51 | 52 | type UnionOf = T extends readonly [Predicate, ...infer R] 53 | ? U | UnionOf 54 | : never; 55 | -------------------------------------------------------------------------------- /is/union_of_bench.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "@std/assert"; 2 | import { isObjectOf } from "./object_of.ts"; 3 | import { isUnionOf as isUnionOf420 } from "jsr:@core/unknownutil@4.2.0/is/union-of"; 4 | import { isUnionOf } from "./union_of.ts"; 5 | 6 | const repeats = Array.from({ length: 100 }); 7 | const positive: unknown = { a: "", b: 0, c: true }; 8 | const positive10: unknown = Object.assign( 9 | { a: "", b: 0, c: true }, 10 | Array.from({ length: 10 }), 11 | ); 12 | const positive1000: unknown = Object.assign( 13 | { a: "", b: 0, c: true }, 14 | Array.from({ length: 1000 }), 15 | ); 16 | const negative: unknown = {}; 17 | const negative10: unknown = Object.assign({}, Array.from({ length: 10 })); 18 | const negative1000: unknown = Object.assign({}, Array.from({ length: 1000 })); 19 | 20 | const preds = [ 21 | isObjectOf({ 22 | a: (x: unknown): x is string => typeof x === "string", 23 | }), 24 | isObjectOf({ 25 | b: (x: unknown): x is number => typeof x === "number", 26 | }), 27 | isObjectOf({ 28 | c: (x: unknown): x is boolean => typeof x === "boolean", 29 | }), 30 | ] as const; 31 | const pred420 = isUnionOf420(preds); 32 | const pred = isUnionOf(preds); 33 | 34 | Deno.bench({ 35 | name: "current", 36 | fn() { 37 | assert(repeats.every(() => pred(positive))); 38 | }, 39 | group: "isUnionOf (positive)", 40 | }); 41 | 42 | Deno.bench({ 43 | name: "current", 44 | fn() { 45 | assert(repeats.every(() => pred(positive10))); 46 | }, 47 | group: "isUnionOf (positive 10)", 48 | }); 49 | 50 | Deno.bench({ 51 | name: "current", 52 | fn() { 53 | assert(repeats.every(() => pred(positive1000))); 54 | }, 55 | group: "isUnionOf (positive 1000)", 56 | }); 57 | 58 | Deno.bench({ 59 | name: "current", 60 | fn() { 61 | assert(repeats.every(() => !pred(negative))); 62 | }, 63 | group: "isUnionOf (negative)", 64 | }); 65 | 66 | Deno.bench({ 67 | name: "current", 68 | fn() { 69 | assert(repeats.every(() => !pred(negative10))); 70 | }, 71 | group: "isUnionOf (negative 10)", 72 | }); 73 | 74 | Deno.bench({ 75 | name: "current", 76 | fn() { 77 | assert(repeats.every(() => !pred(negative1000))); 78 | }, 79 | group: "isUnionOf (negative 1000)", 80 | }); 81 | 82 | Deno.bench({ 83 | name: "v4.2.0", 84 | fn() { 85 | assert(repeats.every(() => pred420(positive))); 86 | }, 87 | group: "isUnionOf (positive)", 88 | }); 89 | 90 | Deno.bench({ 91 | name: "v4.2.0", 92 | fn() { 93 | assert(repeats.every(() => pred420(positive10))); 94 | }, 95 | group: "isUnionOf (positive 10)", 96 | }); 97 | 98 | Deno.bench({ 99 | name: "v4.2.0", 100 | fn() { 101 | assert(repeats.every(() => pred420(positive1000))); 102 | }, 103 | group: "isUnionOf (positive 1000)", 104 | }); 105 | 106 | Deno.bench({ 107 | name: "v4.2.0", 108 | fn() { 109 | assert(repeats.every(() => !pred420(negative))); 110 | }, 111 | group: "isUnionOf (negative)", 112 | }); 113 | 114 | Deno.bench({ 115 | name: "v4.2.0", 116 | fn() { 117 | assert(repeats.every(() => !pred420(negative10))); 118 | }, 119 | group: "isUnionOf (negative 10)", 120 | }); 121 | 122 | Deno.bench({ 123 | name: "v4.2.0", 124 | fn() { 125 | assert(repeats.every(() => !pred420(negative1000))); 126 | }, 127 | group: "isUnionOf (negative 1000)", 128 | }); 129 | -------------------------------------------------------------------------------- /is/union_of_test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from "@std/assert"; 2 | import { assertSnapshot } from "@std/testing/snapshot"; 3 | import { assertType, type IsExact } from "@std/testing/types"; 4 | import { testWithExamples } from "../_testutil.ts"; 5 | import type { PredicateType } from "../type.ts"; 6 | import { is } from "./mod.ts"; 7 | import { isUnionOf } from "./union_of.ts"; 8 | 9 | Deno.test("isUnionOf", async (t) => { 10 | await t.step("returns properly named predicate function", async (t) => { 11 | await assertSnapshot(t, isUnionOf([is.Number, is.String, is.Boolean]).name); 12 | }); 13 | 14 | await t.step("returns true on one of T", () => { 15 | const preds = [is.Number, is.String, is.Boolean] as const; 16 | assertEquals(isUnionOf(preds)(0), true); 17 | assertEquals(isUnionOf(preds)("a"), true); 18 | assertEquals(isUnionOf(preds)(true), true); 19 | }); 20 | 21 | await t.step("returns false on non of T", async (t) => { 22 | const preds = [is.Number, is.String, is.Boolean] as const; 23 | await testWithExamples(t, isUnionOf(preds), { 24 | excludeExamples: ["number", "string", "boolean"], 25 | }); 26 | }); 27 | 28 | await t.step("predicated type is correct", () => { 29 | const preds = [is.Number, is.String, is.Boolean] as const; 30 | const a: unknown = [0, "a", true]; 31 | if (isUnionOf(preds)(a)) { 32 | assertType>(true); 33 | } 34 | }); 35 | 36 | await t.step("predicated type is correct (#49)", () => { 37 | const isFoo = is.ObjectOf({ foo: is.String }); 38 | const isBar = is.ObjectOf({ foo: is.String, bar: is.Number }); 39 | type Foo = PredicateType; 40 | type Bar = PredicateType; 41 | const preds = [isFoo, isBar] as const; 42 | const a: unknown = [0, "a", true]; 43 | if (isUnionOf(preds)(a)) { 44 | assertType>(true); 45 | } 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /is/unknown.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Assume `x` is `unknown` and always return `true` regardless of the type of `x`. 3 | * 4 | * Use {@linkcode [is/any].isAny|isAny} to assume that the type of `x` is `any`. 5 | * 6 | * ```ts 7 | * import { is } from "@core/unknownutil"; 8 | * 9 | * const a = "a"; 10 | * if (is.Unknown(a)) { 11 | * const _: unknown = a; 12 | * } 13 | * ``` 14 | */ 15 | export function isUnknown(_x: unknown): _x is unknown { 16 | return true; 17 | } 18 | -------------------------------------------------------------------------------- /is/unknown_bench.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "@std/assert"; 2 | import { isUnknown as isUnknown420 } from "jsr:@core/unknownutil@4.2.0/is/unknown"; 3 | import { isUnknown } from "./unknown.ts"; 4 | 5 | const repeats = Array.from({ length: 100 }); 6 | const positive: unknown = ""; 7 | 8 | Deno.bench({ 9 | name: "current", 10 | fn() { 11 | assert(repeats.every(() => isUnknown(positive))); 12 | }, 13 | group: "isUnknown (positive)", 14 | }); 15 | 16 | Deno.bench({ 17 | name: "v4.2.0", 18 | fn() { 19 | assert(repeats.every(() => isUnknown420(positive))); 20 | }, 21 | group: "isUnknown (positive)", 22 | }); 23 | -------------------------------------------------------------------------------- /is/unknown_test.ts: -------------------------------------------------------------------------------- 1 | import { testWithExamples } from "../_testutil.ts"; 2 | import { isUnknown } from "./unknown.ts"; 3 | 4 | Deno.test("isUnknown", async (t) => { 5 | await testWithExamples(t, isUnknown, { 6 | validExamples: [ 7 | "string", 8 | "number", 9 | "bigint", 10 | "boolean", 11 | "array", 12 | "set", 13 | "record", 14 | "map", 15 | "syncFunction", 16 | "asyncFunction", 17 | "null", 18 | "undefined", 19 | "symbol", 20 | "date", 21 | "promise", 22 | ], 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /maybe.ts: -------------------------------------------------------------------------------- 1 | import type { Predicate } from "./type.ts"; 2 | 3 | /** 4 | * Returns the input value if it satisfies the provided predicate, or `undefined` otherwise. 5 | * 6 | * ```ts 7 | * import { is, maybe } from "@core/unknownutil"; 8 | * 9 | * const a: unknown = "hello"; 10 | * const _: string = maybe(a, is.String) ?? "default value"; 11 | * ``` 12 | * 13 | * @param x The value to be tested. 14 | * @param pred The predicate function to test the value against. 15 | * @returns The input value `x` if it satisfies the predicate, or `undefined` otherwise. 16 | */ 17 | export function maybe( 18 | x: unknown, 19 | pred: Predicate, 20 | ): T | undefined { 21 | return pred(x) ? x : undefined; 22 | } 23 | -------------------------------------------------------------------------------- /maybe_test.ts: -------------------------------------------------------------------------------- 1 | import { assertStrictEquals } from "@std/assert"; 2 | import { maybe } from "./maybe.ts"; 3 | 4 | const x: unknown = Symbol("x"); 5 | 6 | function truePredicate(_x: unknown): _x is string { 7 | return true; 8 | } 9 | 10 | function falsePredicate(_x: unknown): _x is string { 11 | return false; 12 | } 13 | 14 | Deno.test("maybe", async (t) => { 15 | await t.step("returns `x` as-is on true predicate", () => { 16 | assertStrictEquals(maybe(x, truePredicate), x); 17 | }); 18 | 19 | await t.step("returns `undefined` on false predicate", () => { 20 | assertStrictEquals(maybe(x, falsePredicate), undefined); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /mod.ts: -------------------------------------------------------------------------------- 1 | export type * from "./type.ts"; 2 | 3 | export * from "./as/mod.ts"; 4 | export * from "./is/mod.ts"; 5 | 6 | export * from "./assert.ts"; 7 | export * from "./ensure.ts"; 8 | export * from "./maybe.ts"; 9 | -------------------------------------------------------------------------------- /mod_test.ts: -------------------------------------------------------------------------------- 1 | import { assertArrayIncludes } from "@std/assert"; 2 | import { basename, globToRegExp } from "@std/path"; 3 | import { parse } from "@std/jsonc"; 4 | import { ensure } from "./ensure.ts"; 5 | import { is } from "./is/mod.ts"; 6 | 7 | const excludes = [ 8 | "mod.ts", 9 | "_*.ts", 10 | "*_bench.ts", 11 | "*_test.ts", 12 | ]; 13 | 14 | Deno.test("JSR exports must have all `as` modules", async () => { 15 | const moduleNames = await listModuleNames( 16 | new URL(import.meta.resolve("./as")), 17 | ); 18 | const jsrExports = await loadJsrExports(); 19 | assertArrayIncludes(Object.entries(jsrExports.exports), [ 20 | ["./as", "./as/mod.ts"], 21 | ...moduleNames.map(( 22 | v, 23 | ) => [`./as/${v.replaceAll("_", "-")}`, `./as/${v}.ts`]), 24 | ]); 25 | }); 26 | 27 | Deno.test("JSR exports must have all `is` modules", async () => { 28 | const moduleNames = await listModuleNames( 29 | new URL(import.meta.resolve("./is")), 30 | ); 31 | const jsrExports = await loadJsrExports(); 32 | assertArrayIncludes(Object.entries(jsrExports.exports), [ 33 | ["./is", "./is/mod.ts"], 34 | ...moduleNames.map(( 35 | v, 36 | ) => [`./is/${v.replaceAll("_", "-")}`, `./is/${v}.ts`]), 37 | ]); 38 | }); 39 | 40 | async function listModuleNames(path: URL | string): Promise { 41 | const patterns = excludes.map((p) => globToRegExp(p)); 42 | const names: string[] = []; 43 | for await (const entry of Deno.readDir(path)) { 44 | if (!entry.isFile || !entry.name.endsWith(".ts")) continue; 45 | if (patterns.some((p) => p.test(entry.name))) continue; 46 | names.push(basename(entry.name, ".ts")); 47 | } 48 | return names; 49 | } 50 | 51 | async function loadJsrExports(): Promise<{ exports: Record }> { 52 | const text = await Deno.readTextFile( 53 | new URL(import.meta.resolve("./deno.jsonc")), 54 | ); 55 | const json = ensure( 56 | parse(text), 57 | is.ObjectOf({ 58 | exports: is.RecordOf(is.String, is.String), 59 | }), 60 | ); 61 | return { exports: json.exports }; 62 | } 63 | -------------------------------------------------------------------------------- /type.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * A type predicate function. 3 | * 4 | * ```ts 5 | * import { as, is, type Predicate } from "@core/unknownutil"; 6 | * 7 | * type Person = { 8 | * name: string; 9 | * age: number; 10 | * address?: string | undefined; 11 | * }; 12 | * const isPerson = is.ObjectOf({ 13 | * name: is.String, 14 | * age: is.Number, 15 | * address: as.Optional(is.String), 16 | * }) satisfies Predicate; 17 | * ``` 18 | */ 19 | export type Predicate = (x: unknown) => x is T; 20 | 21 | /** 22 | * A type predicated by {@linkcode Predicate}. 23 | * 24 | * ```ts 25 | * import { as, is, type PredicateType } from "@core/unknownutil"; 26 | * 27 | * const isPerson = is.ObjectOf({ 28 | * name: is.String, 29 | * age: is.Number, 30 | * address: as.Optional(is.String), 31 | * }); 32 | * 33 | * type Person = PredicateType; 34 | * // type Person = { 35 | * // name: string; 36 | * // age: number; 37 | * // address?: string | undefined; 38 | * // }; 39 | * ``` 40 | */ 41 | export type PredicateType

= P extends Predicate ? T : never; 42 | 43 | /** 44 | * JavaScript primitive types. 45 | */ 46 | export type Primitive = 47 | | string 48 | | number 49 | | bigint 50 | | boolean 51 | | null 52 | | undefined 53 | | symbol; 54 | --------------------------------------------------------------------------------