diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ef722c5..820f4fa 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -24,6 +24,10 @@ jobs: run: deno lint - name: Type check run: deno task check + - name: Gen check + run: | + deno task gen + git diff --exit-code test: runs-on: ubuntu-latest diff --git a/.scripts/gen-mod.ts b/.scripts/gen-mod.ts new file mode 100644 index 0000000..1edf172 --- /dev/null +++ b/.scripts/gen-mod.ts @@ -0,0 +1,123 @@ +import { fromFileUrl, globToRegExp, join, relative } from "@std/path"; +import { map } from "@core/iterutil/map"; +import { flatMap } from "@core/iterutil/async/flat-map"; + +const decoder = new TextDecoder(); + +const excludes = [ + "mod.ts", + "*_test.ts", + "*_bench.ts", + "_*.ts", +]; + +type DenoDocEntry = { + name: string; + location: { + filename: string; + }; + declarationKind: string; + jsDoc: { + doc: string; + }; + kind: string; +}; + +function isDenoDocEntry(x: unknown): x is DenoDocEntry { + if (x == null || typeof x !== "object") return false; + if (typeof (x as DenoDocEntry).name !== "string") return false; + if (typeof (x as DenoDocEntry).location !== "object") return false; + if (typeof (x as DenoDocEntry).location.filename !== "string") return false; + if (typeof (x as DenoDocEntry).declarationKind !== "string") return false; + if (typeof (x as DenoDocEntry).jsDoc !== "object") return false; + if (typeof (x as DenoDocEntry).jsDoc.doc !== "string") return false; + if (typeof (x as DenoDocEntry).kind !== "string") return false; + return true; +} + +async function listDenoDocEntries(path: string): Promise { + const cmd = new Deno.Command(Deno.execPath(), { + args: ["doc", "--json", path], + stdout: "piped", + stderr: "piped", + }); + const { success, stdout, stderr } = await cmd.output(); + if (!success) { + throw new Error(decoder.decode(stderr)); + } + const json = JSON.parse(decoder.decode(stdout)); + if (!Array.isArray(json)) { + throw new Error(`Expected array but got ${JSON.stringify(json)}`); + } + return json.filter(isDenoDocEntry); +} + +async function* iterModules(path: string): AsyncIterable { + const patterns = excludes.map((p) => globToRegExp(p)); + for await (const entry of Deno.readDir(path)) { + if (!entry.isFile || !entry.name.endsWith(".ts")) continue; + if (patterns.some((p) => p.test(entry.name))) continue; + yield join(path, entry.name); + } +} + +async function generateModTs( + namespace: string, +): Promise { + const path = fromFileUrl(import.meta.resolve(`../${namespace}/`)); + const exports = (await Array.fromAsync( + flatMap(iterModules(path), (x) => listDenoDocEntries(x)), + )) + .filter((x) => x.kind === "function") + .filter((x) => x.declarationKind === "export") + .filter((x) => x.name.startsWith(namespace)) + .map((x) => ({ + path: relative(path, fromFileUrl(x.location.filename)), + name: x.name, + doc: x.jsDoc.doc, + })) + .toSorted((a, b) => a.name.localeCompare(b.name)); + const lines = [ + "// NOTE: This file is generated by gen-mod.ts", + ...exports.map((x) => { + return `import { ${x.name} } from "./${x.path}";`; + }), + "", + ...map((new Set(exports.map((x) => x.path))).values(), (x) => { + return `export * from "./${x}";`; + }), + "", + "/**", + ` * An object containing all the functions in ${namespace} module.`, + " */", + `export const ${namespace}: {`, + ...exports.flatMap((x) => { + return [ + " /**", + ...x.doc.split("\n").map((line) => ` * ${line}`.trimEnd()), + " */", + ` ${x.name.replace(namespace, "")}: typeof ${x.name};`.trimEnd(), + ]; + }), + "} = {", + ...exports.flatMap((x) => { + return [ + ` ${x.name.replace(namespace, "")}: ${x.name},`.trimEnd(), + ]; + }), + "};", + ]; + await Deno.writeTextFile(join(path, "mod.ts"), lines.join("\n") + "\n"); +} + +async function main(): Promise { + await generateModTs("is"); + await generateModTs("as"); +} + +if (import.meta.main) { + main().catch((err) => { + console.error(err); + Deno.exit(1); + }); +} diff --git a/as/mod.ts b/as/mod.ts index 0f46786..56fab8f 100644 --- a/as/mod.ts +++ b/as/mod.ts @@ -1,10 +1,109 @@ -import { asOptional, asUnoptional } from "./optional.ts"; -import { asReadonly, asUnreadonly } from "./readonly.ts"; +// NOTE: This file is generated by gen-mod.ts +import { asOptional } from "./optional.ts"; +import { asReadonly } from "./readonly.ts"; +import { asUnoptional } from "./optional.ts"; +import { asUnreadonly } from "./readonly.ts"; + +export * from "./optional.ts"; +export * from "./readonly.ts"; /** - * Annotation collection for object predicate properties. + * An object containing all the functions in as module. */ -export const as = { +export const as: { + /** + * Annotate the given predicate function as optional. + * + * Use this function to annotate a predicate function of `predObj` in {@linkcode isObjectOf}. + * + * Note that the annotated predicate function will return `true` if the type of `x` is `T` or `undefined`, indicating that + * this function is not just for annotation but it also changes the behavior of the predicate function. + * + * Use {@linkcode asUnoptional} to remove the annotation. + * Use {@linkcode hasOptional} to check if a predicate function has annotated with this function. + * + * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. + * + * ```ts + * import { as, is } from "@core/unknownutil"; + * + * const isMyType = is.ObjectOf({ + * foo: as.Optional(is.String), + * }); + * const a: unknown = {}; + * if (isMyType(a)) { + * const _: {foo?: string} = a; + * } + * ``` + */ + Optional: typeof asOptional; + /** + * Annotate the given predicate function as readonly. + * + * Use this function to annotate a predicate function of `predObj` in {@linkcode isObjectOf}. + * + * Use {@linkcode asUnreadonly} to remove the annotation. + * Use {@linkcode hasReadonly} to check if a predicate function has annotated with this function. + * + * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. + * + * ```ts + * import { as, is } from "@core/unknownutil"; + * + * const isMyType = is.ObjectOf({ + * foo: as.Readonly(is.String), + * }); + * const a: unknown = {}; + * if (isMyType(a)) { + * const _: {readonly foo: string} = a; + * } + * ``` + */ + Readonly: typeof asReadonly; + /** + * Unannotate the annotated predicate function with {@linkcode asOptional}. + * + * Use this function to unannotate a predicate function of `predObj` in {@linkcode isObjectOf}. + * + * Note that the annotated predicate function will return `true` if the type of `x` is `T`, indicating that + * this function is not just for annotation but it also changes the behavior of the predicate function. + * + * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. + * + * ```ts + * import { as, is } from "@core/unknownutil"; + * + * const isMyType = is.ObjectOf({ + * foo: as.Unoptional(as.Optional(is.String)), + * }); + * const a: unknown = {foo: "a"}; + * if (isMyType(a)) { + * const _: {foo: string} = a; + * } + * ``` + */ + Unoptional: typeof asUnoptional; + /** + * Unannotate the annotated predicate function with {@linkcode asReadonly}. + * + * Use this function to unannotate a predicate function of `predObj` in {@linkcode isObjectOf}. + * + * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. + * + * ```ts + * import { as, is } from "@core/unknownutil"; + * + * const isMyType = is.ObjectOf({ + * foo: as.Unreadonly(as.Readonly(is.String)), + * }); + * const a: unknown = {foo: "a"}; + * if (isMyType(a)) { + * const _: {foo: string} = a; + * } + * ``` + */ + Unreadonly: typeof asUnreadonly; +} = { Optional: asOptional, Readonly: asReadonly, Unoptional: asUnoptional, diff --git a/deno.jsonc b/deno.jsonc index 33da01f..44a3055 100644 --- a/deno.jsonc +++ b/deno.jsonc @@ -66,6 +66,7 @@ ] }, "imports": { + "@core/iterutil": "jsr:@core/iterutil@^0.3.0", "@core/unknownutil": "./mod.ts", "@deno/dnt": "jsr:@deno/dnt@^0.41.1", "@std/assert": "jsr:@std/assert@^0.221.0", @@ -78,6 +79,7 @@ "test": "deno test -A --doc --parallel --shuffle", "test:coverage": "deno task test --coverage=.coverage", "coverage": "deno coverage .coverage", + "gen": "deno run --allow-run=deno --allow-read --allow-write=. .scripts/gen-mod.ts", "update": "deno run --allow-env --allow-read --allow-write=. --allow-run=git,deno --allow-net=jsr.io,registry.npmjs.org jsr:@molt/cli ./*.ts", "update:commit": "deno task -q update --commit --prefix deps: --pre-commit=fmt,lint" } diff --git a/is/mod.ts b/is/mod.ts index f99ba41..e6a920e 100644 --- a/is/mod.ts +++ b/is/mod.ts @@ -1,3 +1,4 @@ +// NOTE: This file is generated by gen-mod.ts import { isAny } from "./any.ts"; import { isArray } from "./array.ts"; import { isArrayOf } from "./array_of.ts"; @@ -38,10 +39,927 @@ import { isUniformTupleOf } from "./uniform_tuple_of.ts"; import { isUnionOf } from "./union_of.ts"; import { isUnknown } from "./unknown.ts"; +export * from "./any.ts"; +export * from "./array.ts"; +export * from "./array_of.ts"; +export * from "./async_function.ts"; +export * from "./bigint.ts"; +export * from "./boolean.ts"; +export * from "./function.ts"; +export * from "./instance_of.ts"; +export * from "./intersection_of.ts"; +export * from "./literal_of.ts"; +export * from "./literal_one_of.ts"; +export * from "./map.ts"; +export * from "./map_of.ts"; +export * from "./null.ts"; +export * from "./nullish.ts"; +export * from "./number.ts"; +export * from "./object_of.ts"; +export * from "./omit_of.ts"; +export * from "./parameters_of.ts"; +export * from "./partial_of.ts"; +export * from "./pick_of.ts"; +export * from "./primitive.ts"; +export * from "./readonly_of.ts"; +export * from "./record.ts"; +export * from "./record_object.ts"; +export * from "./record_object_of.ts"; +export * from "./record_of.ts"; +export * from "./required_of.ts"; +export * from "./set.ts"; +export * from "./set_of.ts"; +export * from "./strict_of.ts"; +export * from "./string.ts"; +export * from "./symbol.ts"; +export * from "./sync_function.ts"; +export * from "./tuple_of.ts"; +export * from "./undefined.ts"; +export * from "./uniform_tuple_of.ts"; +export * from "./union_of.ts"; +export * from "./unknown.ts"; + /** - * Type predicate function collection. + * An object containing all the functions in is module. */ -export const is = { +export const is: { + /** + * Assume `x is `any` and always return `true` regardless of the type of `x`. + * + * Use {@linkcode isUnknown} to assume that a value is `unknown`. + * + * ```ts + * import { is } from "@core/unknownutil"; + * + * const a = "a"; + * if (is.Any(a)) { + * const _: any = a; + * } + * ``` + */ + Any: typeof isAny; + /** + * Return `true` if the type of `x` is `unknown[]`. + * + * Use {@linkcode isArrayOf} to check if the type of `x` is an array of `T`. + * + * ```ts + * import { is } from "@core/unknownutil"; + * + * const a: unknown = [0, 1, 2]; + * if (is.Array(a)) { + * const _: unknown[] = a; + * } + * ``` + */ + Array: typeof isArray; + /** + * Return a type predicate function that returns `true` if the type of `x` is `T[]`. + * + * Use {@linkcode isArray} to check if the type of `x` is an array of `unknown`. + * + * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. + * + * ```ts + * import { is } from "@core/unknownutil"; + * + * const isMyType = is.ArrayOf(is.String); + * const a: unknown = ["a", "b", "c"]; + * if (isMyType(a)) { + * const _: string[] = a; + * } + * ``` + */ + ArrayOf: typeof isArrayOf; + /** + * Return `true` if the type of `x` is `function` (async function). + * + * Use {@linkcode isFunction} to check if the type of `x` is a function. + * Use {@linkcode isSyncFunction} to check if the type of `x` is a synchronous function. + * + * ```ts + * import { is } from "@core/unknownutil"; + * + * const a: unknown = async () => {}; + * if (is.AsyncFunction(a)) { + * const _: ((...args: unknown[]) => Promise) = a; + * } + * ``` + */ + AsyncFunction: typeof isAsyncFunction; + /** + * Return `true` if the type of `x` is `bigint`. + * + * ```ts + * import { is } from "@core/unknownutil"; + * + * const a: unknown = 0n; + * if (is.Bigint(a)) { + * const _: bigint = a; + * } + * ``` + */ + Bigint: typeof isBigint; + /** + * Return `true` if the type of `x` is `boolean`. + * + * ```ts + * import { is } from "@core/unknownutil"; + * + * const a: unknown = true; + * if (is.Boolean(a)) { + * const _: boolean = a; + * } + * ``` + */ + Boolean: typeof isBoolean; + /** + * Return `true` if the type of `x` is `function`. + * + * Use {@linkcode isSyncFunction} to check if the type of `x` is a synchronous function. + * Use {@linkcode isAsyncFunction} to check if the type of `x` is an asynchronous function. + * + * ```ts + * import { is } from "@core/unknownutil"; + * + * const a: unknown = () => {}; + * if (is.Function(a)) { + * const _: ((...args: unknown[]) => unknown) = a; + * } + * ``` + */ + Function: typeof isFunction; + /** + * Return `true` if the type of `x` is instance of `ctor`. + * + * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. + * + * ```ts + * import { is } from "@core/unknownutil"; + * + * const isMyType = is.InstanceOf(Date); + * const a: unknown = new Date(); + * if (isMyType(a)) { + * const _: Date = a; + * } + * ``` + */ + InstanceOf: typeof isInstanceOf; + /** + * Return a type predicate function that returns `true` if the type of `x` is `IntersectionOf`. + * + * Use {@linkcode isUnionOf} to check if the type of `x` is a union of `T`. + * + * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. + * + * ```ts + * import { is } from "@core/unknownutil"; + * + * const isMyType = is.IntersectionOf([ + * is.ObjectOf({ a: is.Number }), + * is.ObjectOf({ b: is.String }), + * ]); + * const a: unknown = { a: 0, b: "a" }; + * if (isMyType(a)) { + * const _: { a: number } & { b: string } = a; + * } + * ``` + * + * Depending on the version of TypeScript and how values are provided, it may be necessary to add `as const` to the array + * used as `preds`. If a type error occurs, try adding `as const` as follows: + * + * ```ts + * import { is } from "@core/unknownutil"; + * + * const preds = [ + * is.ObjectOf({ a: is.Number }), + * is.ObjectOf({ b: is.String }), + * ] as const + * const isMyType = is.IntersectionOf(preds); + * const a: unknown = { a: 0, b: "a" }; + * if (isMyType(a)) { + * const _: { a: number } & { b: string } = a; + * } + * ``` + */ + IntersectionOf: typeof isIntersectionOf; + /** + * Return a type predicate function that returns `true` if the type of `x` is a literal type of `pred`. + * + * Use {@linkcode isLiteral} to check if the type of `x` is a literal type. + * Use {@linkcode isLiteralOneOf} to check if the type of `x` is one of the literal type of `Primitive[]`. + * + * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. + * + * ```ts + * import { is } from "@core/unknownutil"; + * + * const isMyType = is.LiteralOf("hello"); + * const a: unknown = "hello"; + * if (isMyType(a)) { + * const _: "hello" = a; + * } + * ``` + */ + LiteralOf: typeof isLiteralOf; + /** + * Return a type predicate function that returns `true` if the type of `x` is one of literal type in `preds`. + * + * Use {@linkcode isLiteral} to check if the type of `x` is a literal type. + * Use {@linkcode isLiteralOf} to check if the type of `x` is a literal type of `Primitive`. + * + * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. + * + * ```ts + * import { is } from "@core/unknownutil"; + * + * const isMyType = is.LiteralOneOf(["hello", "world"] as const); + * const a: unknown = "hello"; + * if (isMyType(a)) { + * const _: "hello" | "world" = a; + * } + * ``` + */ + LiteralOneOf: typeof isLiteralOneOf; + /** + * Return `true` if the type of `x` is `Map`. + * + * Use {@linkcode isMapOf} to check if the type of `x` is a map of `T`. + * + * ```ts + * import { is } from "@core/unknownutil"; + * + * const a: unknown = new Map([["a", 0], ["b", 1]]); + * if (is.Map(a)) { + * const _: Map = a; + * } + * ``` + */ + Map: typeof isMap; + /** + * Return a type predicate function that returns `true` if the type of `x` is `Map`. + * + * Use {@linkcode isMap} to check if the type of `x` is a map of `unknown`. + * + * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. + * + * ```ts + * import { is } from "@core/unknownutil"; + * + * const isMyType = is.MapOf(is.Number); + * const a: unknown = new Map([["a", 0], ["b", 1]]); + * if (isMyType(a)) { + * const _: Map = a; + * } + * ``` + * + * With predicate function for keys: + * + * ```ts + * import { is } from "@core/unknownutil"; + * + * const isMyType = is.MapOf(is.Number, is.String); + * const a: unknown = new Map([["a", 0], ["b", 1]]); + * if (isMyType(a)) { + * const _: Map = a; + * } + * ``` + */ + MapOf: typeof isMapOf; + /** + * Return `true` if the type of `x` is `null`. + * + * Use {@linkcode isUndefined} to check if the type of `x` is `undefined`. + * Use {@linkcode isNullish} to check if the type of `x` is `null` or `undefined`. + * + * ```ts + * import { is } from "@core/unknownutil"; + * + * const a: unknown = null; + * if (is.Null(a)) { + * const _: null = a; + * } + * ``` + */ + Null: typeof isNull; + /** + * Return `true` if the type of `x` is `null` or `undefined`. + * + * Use {@linkcode isNull} to check if the type of `x` is `null`. + * Use {@linkcode isUndefined} to check if the type of `x` is `undefined`. + * + * ```ts + * import { is } from "@core/unknownutil"; + * + * const a: unknown = null; + * if (is.Nullish(a)) { + * const _: (null | undefined) = a; + * } + * ``` + */ + Nullish: typeof isNullish; + /** + * Return `true` if the type of `x` is `number`. + * + * ```ts + * import { is } from "@core/unknownutil"; + * + * const a: unknown = 0; + * if (is.Number(a)) { + * const _: number = a; + * } + * ``` + */ + Number: typeof isNumber; + /** + * Return a type predicate function that returns `true` if the type of `x` is `ObjectOf`. + * + * Use {@linkcode isRecordOf} if you want to check if the type of `x` is a record of `T`. + * + * If {@linkcode asOptional} is specified in the predicate function in `predObj`, the property becomes optional. + * If {@linkcode asReadonly} is specified in the predicate function in `predObj`, the property becomes readonly. + * + * The number of keys of `x` must be greater than or equal to the number of keys of `predObj`. + * Use {@linkcode isStrictOf} if you want to check the exact number of keys. + * + * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. + * + * ```ts + * import { as, is } from "@core/unknownutil"; + * + * const isMyType = is.ObjectOf({ + * a: is.Number, + * b: is.String, + * c: as.Optional(is.Boolean), + * d: as.Readonly(is.String), + * }); + * const a: unknown = { a: 0, b: "a", d: "d" }; + * if (isMyType(a)) { + * const _: { a: number; b: string; c?: boolean | undefined, readonly d: string } = a; + * } + * ``` + */ + ObjectOf: typeof isObjectOf; + /** + * Return a type predicate function that returns `true` if the type of `x` is `Omit, K>`. + * + * It only supports modifing a predicate function annotated with `IsPredObj`, usually returned by the followings + * + * - {@linkcode isIntersectionOf} + * - {@linkcode isObjectOf} + * - {@linkcode isOmitOf} + * - {@linkcode isPartialOf} + * - {@linkcode isPickOf} + * - {@linkcode isReadonlyOf} + * - {@linkcode isRequiredOf} + * - {@linkcode isStrictOf} + * + * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. + * + * ```typescript + * import { as, is } from "@core/unknownutil"; + * + * const isMyType = is.OmitOf(is.ObjectOf({ + * a: is.Number, + * b: is.String, + * c: as.Optional(is.Boolean), + * }), ["a", "c"]); + * const a: unknown = { a: 0, b: "a" }; + * if (isMyType(a)) { + * const _: { b: string } = a; + * } + * ``` + */ + OmitOf: typeof isOmitOf; + /** + * Return a type predicate function that returns `true` if the type of `x` is `ParametersOf` or `ParametersOf`. + * + * This is similar to {@linkcode isTupleOf}, but if {@linkcode asOptional} is specified at the trailing, the trailing elements becomes optional and makes variable-length tuple. + * + * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. + * + * ```ts + * import { as, is } from "@core/unknownutil"; + * + * const isMyType = is.ParametersOf([ + * is.Number, + * as.Optional(is.String), + * is.Boolean, + * as.Optional(is.Number), + * as.Optional(is.String), + * as.Optional(is.Boolean), + * ] as const); + * const a: unknown = [0, undefined, "a"]; + * if (isMyType(a)) { + * const _: [number, string | undefined, boolean, number?, string?, boolean?] = a; + * } + * ``` + * + * With `predElse`: + * + * ```ts + * import { as, is } from "@core/unknownutil"; + * + * const isMyType = is.ParametersOf( + * [ + * is.Number, + * as.Optional(is.String), + * as.Optional(is.Boolean), + * ] as const, + * is.ArrayOf(is.Number), + * ); + * const a: unknown = [0, "a", true, 0, 1, 2]; + * if (isMyType(a)) { + * const _: [number, string?, boolean?, ...number[]] = a; + * } + * ``` + * + * Depending on the version of TypeScript and how values are provided, it may be necessary to add `as const` to the array + * used as `predTup`. If a type error occurs, try adding `as const` as follows: + * + * ```ts + * import { as, is } from "@core/unknownutil"; + * + * const predTup = [is.Number, is.String, as.Optional(is.Boolean)] as const; + * const isMyType = is.ParametersOf(predTup); + * const a: unknown = [0, "a"]; + * if (isMyType(a)) { + * const _: [number, string, boolean?] = a; + * } + * ``` + */ + ParametersOf: typeof isParametersOf; + /** + * Return a type predicate function that returns `true` if the type of `x` is `Partial>`. + * + * It only supports modifing a predicate function annotated with `IsPredObj`, usually returned by the followings + * + * - {@linkcode isIntersectionOf} + * - {@linkcode isObjectOf} + * - {@linkcode isOmitOf} + * - {@linkcode isPartialOf} + * - {@linkcode isPickOf} + * - {@linkcode isReadonlyOf} + * - {@linkcode isRequiredOf} + * - {@linkcode isStrictOf} + * + * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. + * + * ```typescript + * import { as, is } from "@core/unknownutil"; + * + * const isMyType = is.PartialOf(is.ObjectOf({ + * a: is.Number, + * b: is.UnionOf([is.String, is.Undefined]), + * c: as.Optional(is.Boolean), + * })); + * const a: unknown = { a: undefined, other: "other" }; + * if (isMyType(a)) { + * const _: { a?: number | undefined; b?: string | undefined; c?: boolean | undefined } = a; + * } + * ``` + */ + PartialOf: typeof isPartialOf; + /** + * Return a type predicate function that returns `true` if the type of `x` is `Pick, K>`. + * + * It only supports modifing a predicate function annotated with `IsPredObj`, usually returned by the followings + * + * - {@linkcode isIntersectionOf} + * - {@linkcode isObjectOf} + * - {@linkcode isOmitOf} + * - {@linkcode isPartialOf} + * - {@linkcode isPickOf} + * - {@linkcode isReadonlyOf} + * - {@linkcode isRequiredOf} + * - {@linkcode isStrictOf} + * + * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. + * + * ```typescript + * import { as, is } from "@core/unknownutil"; + * + * const isMyType = is.PickOf(is.ObjectOf({ + * a: is.Number, + * b: is.String, + * c: as.Optional(is.Boolean), + * }), ["a", "c"]); + * const a: unknown = { a: 0, b: "a" }; + * if (isMyType(a)) { + * const _: { a: number; c?: boolean | undefined } = a; + * } + * ``` + */ + PickOf: typeof isPickOf; + /** + * Return `true` if the type of `x` is `Primitive`. + * + * ```ts + * import { is, type Primitive } from "@core/unknownutil"; + * + * const a: unknown = 0; + * if (is.Primitive(a)) { + * const _: Primitive = a; + * } + * ``` + */ + Primitive: typeof isPrimitive; + /** + * Return a type predicate function that returns `true` if the type of `x` is `Readonly>`. + * + * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. + * + * ```typescript + * import { as, is } from "@core/unknownutil"; + * + * const isMyType = is.ReadonlyOf(is.ObjectOf({ + * a: is.Number, + * b: is.UnionOf([is.String, is.Undefined]), + * c: as.Readonly(is.Boolean), + * })); + * const a: unknown = { a: 0, b: "b", c: true }; + * if (isMyType(a)) { + * const _: { readonly a: number; readonly b: string | undefined; readonly c: boolean } = a; + * } + * ``` + */ + ReadonlyOf: typeof isReadonlyOf; + /** + * Return `true` if the type of `x` satisfies `Record`. + * + * Note that this function returns `true` for ambiguous instances like `Set`, `Map`, `Date`, `Promise`, etc. + * Use {@linkcode isRecordObject} instead if you want to check if `x` is an instance of `Object`. + * + * ```ts + * import { is } from "@core/unknownutil"; + * + * const a: unknown = {"a": 0, "b": 1}; + * if (is.Record(a)) { + * const _: Record = a; + * } + * + * const b: unknown = new Set(); + * if (is.Record(b)) { + * const _: Record = b; + * } + * ``` + */ + Record: typeof isRecord; + /** + * Return `true` if the type of `x` is an object instance that satisfies `Record`. + * + * Note that this function check if the `x` is an instance of `Object`. + * Use {@linkcode isRecord} instead if you want to check if the `x` satisfies the `Record` type. + * + * ```ts + * import { is } from "@core/unknownutil"; + * + * const a: unknown = {"a": 0, "b": 1}; + * if (is.RecordObject(a)) { + * const _: Record = a; + * } + * + * const b: unknown = new Set(); + * if (is.RecordObject(b)) { + * // b is not a raw object, so it is not narrowed + * } + * ``` + */ + RecordObject: typeof isRecordObject; + /** + * Return a type predicate function that returns `true` if the type of `x` is an Object instance that satisfies `Record`. + * + * Note that this function check if the `x` is an instance of `Object`. + * Use {@linkcode isRecordOf} instead if you want to check if the `x` satisfies the `Record` type. + * + * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. + * + * ```ts + * import { is } from "@core/unknownutil"; + * + * const isMyType = is.RecordObjectOf(is.Number); + * const a: unknown = {"a": 0, "b": 1}; + * if (isMyType(a)) { + * const _: Record = a; + * } + * ``` + * + * With predicate function for keys: + * + * ```ts + * import { is } from "@core/unknownutil"; + * + * const isMyType = is.RecordObjectOf(is.Number, is.String); + * const a: unknown = {"a": 0, "b": 1}; + * if (isMyType(a)) { + * const _: Record = a; + * } + * ``` + */ + RecordObjectOf: typeof isRecordObjectOf; + /** + * Return a type predicate function that returns `true` if the type of `x` satisfies `Record`. + * + * Note that this function only check if the `x` satisfies the `Record` type. + * Use {@linkcode isRecordObjectOf} instead if you want to check if the `x` is an instance of `Object`. + * + * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. + * + * ```ts + * import { is } from "@core/unknownutil"; + * + * const isMyType = is.RecordOf(is.Number); + * const a: unknown = {"a": 0, "b": 1}; + * if (isMyType(a)) { + * const _: Record = a; + * } + * ``` + * + * With predicate function for keys: + * + * ```ts + * import { is } from "@core/unknownutil"; + * + * const isMyType = is.RecordOf(is.Number, is.String); + * const a: unknown = {"a": 0, "b": 1}; + * if (isMyType(a)) { + * const _: Record = a; + * } + * ``` + */ + RecordOf: typeof isRecordOf; + /** + * Return a type predicate function that returns `true` if the type of `x` is `Required>`. + * + * It only supports modifing a predicate function annotated with `IsPredObj`, usually returned by the followings + * + * - {@linkcode isIntersectionOf} + * - {@linkcode isObjectOf} + * - {@linkcode isOmitOf} + * - {@linkcode isPartialOf} + * - {@linkcode isPickOf} + * - {@linkcode isReadonlyOf} + * - {@linkcode isRequiredOf} + * - {@linkcode isStrictOf} + * + * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. + * + * ```typescript + * import { as, is } from "@core/unknownutil"; + * + * const isMyType = is.RequiredOf(is.ObjectOf({ + * a: is.Number, + * b: is.UnionOf([is.String, is.Undefined]), + * c: as.Optional(is.Boolean), + * })); + * const a: unknown = { a: 0, b: "b", c: true, other: "other" }; + * if (isMyType(a)) { + * const _: { a: number; b: string | undefined; c: boolean } = a; + * } + * ``` + */ + RequiredOf: typeof isRequiredOf; + /** + * Return `true` if the type of `x` is `Set`. + * + * Use {@linkcode isSetOf} to check if the type of `x` is a set of `T`. + * + * ```ts + * import { is } from "@core/unknownutil"; + * + * const a: unknown = new Set([0, 1, 2]); + * if (is.Set(a)) { + * const _: Set = a; + * } + * ``` + */ + Set: typeof isSet; + /** + * Return a type predicate function that returns `true` if the type of `x` is `Set`. + * + * Use {@linkcode isSet} to check if the type of `x` is a set of `unknown`. + * + * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. + * + * ```ts + * import { is } from "@core/unknownutil"; + * + * const isMyType = is.SetOf(is.String); + * const a: unknown = new Set(["a", "b", "c"]); + * if (isMyType(a)) { + * const _: Set = a; + * } + * ``` + */ + SetOf: typeof isSetOf; + /** + * Return a type predicate function that returns `true` if the type of `x` is strictly follow the `ObjectOf`. + * + * It only supports modifing a predicate function annotated with `IsPredObj`, usually returned by the followings + * + * - {@linkcode isIntersectionOf} + * - {@linkcode isObjectOf} + * - {@linkcode isOmitOf} + * - {@linkcode isPartialOf} + * - {@linkcode isPickOf} + * - {@linkcode isReadonlyOf} + * - {@linkcode isRequiredOf} + * - {@linkcode isStrictOf} + * + * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. + * + * ```ts + * import { as, is } from "@core/unknownutil"; + * + * const isMyType = is.StrictOf(is.ObjectOf({ + * a: is.Number, + * b: is.String, + * c: as.Optional(is.Boolean), + * })); + * const a: unknown = { a: 0, b: "a", other: "other" }; + * if (isMyType(a)) { + * // This block will not be executed because of "other" key in `a`. + * } + * ``` + */ + StrictOf: typeof isStrictOf; + /** + * Return `true` if the type of `x` is `string`. + * + * ```ts + * import { is } from "@core/unknownutil"; + * + * const a: unknown = "a"; + * if (is.String(a)) { + * const _: string = a; + * } + * ``` + */ + String: typeof isString; + /** + * Return `true` if the type of `x` is `symbol`. + * + * ```ts + * import { is } from "@core/unknownutil"; + * + * const a: unknown = Symbol("symbol"); + * if (is.Symbol(a)) { + * const _: symbol = a; + * } + * ``` + */ + Symbol: typeof isSymbol; + /** + * Return `true` if the type of `x` is `function` (non async function). + * + * ```ts + * import { is } from "@core/unknownutil"; + * + * const a: unknown = () => {}; + * if (is.SyncFunction(a)) { + * const _: ((...args: unknown[]) => unknown) = a; + * } + * ``` + */ + SyncFunction: typeof isSyncFunction; + /** + * Return a type predicate function that returns `true` if the type of `x` is `TupleOf` or `TupleOf`. + * + * Use {@linkcode isUniformTupleOf} to check if the type of `x` is a tuple of uniform types. + * + * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. + * + * ```ts + * import { is } from "@core/unknownutil"; + * + * const isMyType = is.TupleOf([is.Number, is.String, is.Boolean]); + * const a: unknown = [0, "a", true]; + * if (isMyType(a)) { + * const _: [number, string, boolean] = a; + * } + * ``` + * + * With `predElse`: + * + * ```ts + * import { is } from "@core/unknownutil"; + * + * const isMyType = is.TupleOf( + * [is.Number, is.String, is.Boolean], + * is.ArrayOf(is.Number), + * ); + * const a: unknown = [0, "a", true, 0, 1, 2]; + * if (isMyType(a)) { + * const _: [number, string, boolean, ...number[]] = a; + * } + * ``` + * + * Depending on the version of TypeScript and how values are provided, it may be necessary to add `as const` to the array + * used as `predTup`. If a type error occurs, try adding `as const` as follows: + * + * ```ts + * import { is } from "@core/unknownutil"; + * + * const predTup = [is.Number, is.String, is.Boolean] as const; + * const isMyType = is.TupleOf(predTup); + * const a: unknown = [0, "a", true]; + * if (isMyType(a)) { + * const _: [number, string, boolean] = a; + * } + * ``` + */ + TupleOf: typeof isTupleOf; + /** + * Return `true` if the type of `x` is `undefined`. + * + * ```ts + * import { is } from "@core/unknownutil"; + * + * const a: unknown = undefined; + * if (is.Undefined(a)) { + * const _: undefined = a; + * } + * ``` + */ + Undefined: typeof isUndefined; + /** + * Return a type predicate function that returns `true` if the type of `x` is `UniformTupleOf`. + * + * Use {@linkcode isTupleOf} to check if the type of `x` is a tuple of `T`. + * + * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. + * + * ```ts + * import { is } from "@core/unknownutil"; + * + * const isMyType = is.UniformTupleOf(5); + * const a: unknown = [0, 1, 2, 3, 4]; + * if (isMyType(a)) { + * const _: [unknown, unknown, unknown, unknown, unknown] = a; + * } + * ``` + * + * With predicate function: + * + * ```ts + * import { is } from "@core/unknownutil"; + * + * const isMyType = is.UniformTupleOf(5, is.Number); + * const a: unknown = [0, 1, 2, 3, 4]; + * if (isMyType(a)) { + * const _: [number, number, number, number, number] = a; + * } + * ``` + */ + UniformTupleOf: typeof isUniformTupleOf; + /** + * Return a type predicate function that returns `true` if the type of `x` is `UnionOf`. + * + * Use {@linkcode isIntersectionOf} to check if the type of `x` is an intersection of `T`. + * + * To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost. + * + * ```ts + * import { is } from "@core/unknownutil"; + * + * const isMyType = is.UnionOf([is.Number, is.String, is.Boolean]); + * const a: unknown = 0; + * if (isMyType(a)) { + * const _: number | string | boolean = a; + * } + * ``` + * + * Depending on the version of TypeScript and how values are provided, it may be necessary to add `as const` to the array + * used as `preds`. If a type error occurs, try adding `as const` as follows: + * + * ```ts + * import { is } from "@core/unknownutil"; + * + * const preds = [is.Number, is.String, is.Boolean] as const; + * const isMyType = is.UnionOf(preds); + * const a: unknown = 0; + * if (isMyType(a)) { + * const _: number | string | boolean = a; + * } + * ``` + */ + UnionOf: typeof isUnionOf; + /** + * Assume `x` is `unknown` and always return `true` regardless of the type of `x`. + * + * Use {@linkcode isAny} to assume that the type of `x` is `any`. + * + * ```ts + * import { is } from "@core/unknownutil"; + * + * const a = "a"; + * if (is.Unknown(a)) { + * const _: unknown = a; + * } + * ``` + */ + Unknown: typeof isUnknown; +} = { Any: isAny, Array: isArray, ArrayOf: isArrayOf, @@ -81,4 +999,4 @@ export const is = { UniformTupleOf: isUniformTupleOf, UnionOf: isUnionOf, Unknown: isUnknown, -} as const; +};