From 66a68006ea26bd25cbd7a7ce70a03cc3e3734e57 Mon Sep 17 00:00:00 2001 From: Andrew Jarrett Date: Mon, 15 Apr 2024 12:44:33 -0500 Subject: [PATCH] feat: adds `Widen` --- .changeset/khaki-squids-deliver.md | 5 ++ src/exports.ts | 2 + src/function/function.ts | 2 +- src/widen/exports.ts | 1 + src/widen/widen.spec.ts | 128 +++++++++++++++++++++++++++++ src/widen/widen.ts | 55 +++++++++++++ 6 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 .changeset/khaki-squids-deliver.md create mode 100644 src/widen/exports.ts create mode 100644 src/widen/widen.spec.ts create mode 100644 src/widen/widen.ts diff --git a/.changeset/khaki-squids-deliver.md b/.changeset/khaki-squids-deliver.md new file mode 100644 index 0000000..dd4f169 --- /dev/null +++ b/.changeset/khaki-squids-deliver.md @@ -0,0 +1,5 @@ +--- +"any-ts": patch +--- + +feat: adds `Widen` type + ambient namespace diff --git a/src/exports.ts b/src/exports.ts index 4f89c53..3385a46 100644 --- a/src/exports.ts +++ b/src/exports.ts @@ -60,3 +60,5 @@ export { Focus, type FocusConstructor } from "./lens/focus" + +export type { Widen } from "./widen/exports" diff --git a/src/function/function.ts b/src/function/function.ts index 9206527..1f9da40 100644 --- a/src/function/function.ts +++ b/src/function/function.ts @@ -4,4 +4,4 @@ export { } type return_ = [type] extends [(...args: any) => infer out] ? out : never -type arguments_ = [type] extends [(...args: infer arguments) => any] ? arguments : never \ No newline at end of file +type arguments_ = [type] extends [(...args: infer arguments) => any] ? arguments : never diff --git a/src/widen/exports.ts b/src/widen/exports.ts new file mode 100644 index 0000000..7283fb8 --- /dev/null +++ b/src/widen/exports.ts @@ -0,0 +1 @@ +export type { Widen } from "./widen" diff --git a/src/widen/widen.spec.ts b/src/widen/widen.spec.ts new file mode 100644 index 0000000..86a1eea --- /dev/null +++ b/src/widen/widen.spec.ts @@ -0,0 +1,128 @@ +import type { Widen } from "./widen" +import type { expect, assert } from "../test/exports" + +declare namespace Spec { + + type __Widen__ = [ + // ^? + expect, + [1, [2, [3, [4, [5, [6]]]]]] + >>, + + expect, + [number, [2, [3, [4, [5, [6]]]]]] + >>, + + expect, + [number, [number, [3, [4, [5, [6]]]]]] + >>, + + expect, + [number, [number, [number, [4, [5, [6]]]]]] + >>, + + expect, + [number, [number, [number, [number, [5, [6]]]]]] + >>, + + expect, + [number, [number, [number, [number, [number, [6]]]]]] + >>, + + expect, + [number, [number, [number, [number, [number, [number]]]]]] + >>, + + expect, + { abc: 1, def: { ghi: 2, jkl: { mno: 3, pqr: { stu: 4, vwx: { yz: 5 } } } } } + >>, + + expect, + { abc: number, def: { ghi: 2, jkl: { mno: 3, pqr: { stu: 4, vwx: { yz: 5 } } } } } + >>, + + expect, + { abc: number, def: { ghi: number, jkl: { mno: 3, pqr: { stu: 4, vwx: { yz: 5 } } } } } + >>, + + expect, + { abc: number, def: { ghi: number, jkl: { mno: number, pqr: { stu: 4, vwx: { yz: 5 } } } } } + >>, + + expect, + { abc: number, def: { ghi: number, jkl: { mno: number, pqr: { stu: number, vwx: { yz: 5 } } } } } + >>, + + expect, + { abc: number, def: { ghi: number, jkl: { mno: number, pqr: { stu: number, vwx: { yz: number } } } } } + >>, + + expect, + { abc: [1, { ghi: [2, { mno: [3, pqr: { stu: [4, vwx: { yz: [5] }] }] }] }] } + >>, + + expect, + { abc: [1, { ghi: [2, { mno: [3, pqr: { stu: [4, vwx: { yz: [5] }] }] }] }] } + >>, + + expect, + { abc: [number, { ghi: [2, { mno: [3, pqr: { stu: [4, vwx: { yz: [5] }] }] }] }] } + >>, + + expect, + { abc: [number, { ghi: [2, { mno: [3, pqr: { stu: [4, vwx: { yz: [5] }] }] }] }] } + >>, + + expect, + { abc: [number, { ghi: [number, { mno: [3, pqr: { stu: [4, vwx: { yz: [5] }] }] }] }] } + >>, + + expect, + { abc: [number, { ghi: [number, { mno: [3, pqr: { stu: [4, vwx: { yz: [5] }] }] }] }] } + >>, + + expect, + { abc: [number, { ghi: [number, { mno: [number, pqr: { stu: [4, vwx: { yz: [5] }] }] }] }] } + >>, + + expect, + { abc: [number, { ghi: [number, { mno: [number, pqr: { stu: [4, vwx: { yz: [5] }] }] }] }] } + >>, + + expect, + { abc: [number, { ghi: [number, { mno: [number, pqr: { stu: [number, vwx: { yz: [5] }] }] }] }] } + >>, + + expect, + { abc: [number, { ghi: [number, { mno: [number, pqr: { stu: [number, vwx: { yz: [5] }] }] }] }] } + >>, + + expect, + { abc: [number, { ghi: [number, { mno: [number, pqr: { stu: [number, vwx: { yz: [number] }] }] }] }] } + >>, + ] +} \ No newline at end of file diff --git a/src/widen/widen.ts b/src/widen/widen.ts new file mode 100644 index 0000000..b450f74 --- /dev/null +++ b/src/widen/widen.ts @@ -0,0 +1,55 @@ +import type { any } from "../any/exports" +import type { nonempty } from "../empty" + +/** + * {@link Widen `Widen`} is a type constructor that accepts a tree and an optional max depth + * (default to `2`), and constructs a new tree with the same overall structure. + * + * Until it hits its max depth, any leaf it encounters is widened to the least upper bound of + * its respective domain. + * + * For example: + * + * - given `{ abc: ["def", "ghi"] }`, {@link Widen `Widen`} returns `{ abc: [string, string] }`; + * - given `[{ abc: 123 }, { def: 456 }]`, {@link Widen `Widen`} returns `[{ abc: number }, { def: number }]` + * + * @example + * import type { Widen } from "any-ts" + * + * type StateTuple = readonly [state: S, setState: S | ((s: S) => S)] + * declare function useState(initialState: Widen): StateTuple> + * + * const [state] = useState([ + * { user: { first: "Caril Ann", last: "Fugate", email: "ms.nat-born-killer@yahoo.com" } }, + * { user: { first: "Charles", last: "Starkweather", email: "mr.nat-born-killer@yahoo.com" } } + * ]) + * + * state + * // ^? [{ user: { first: string, last: string, email: string } }, { user: { first: string, last: string, email: string } }] + */ +export type Widen = Widen.tree + +export declare namespace Widen { + type leaf = type extends { valueOf(): infer target } ? target : type + type tree = internal.widen +} + +declare namespace internal { + type widen + = type extends any.primitive ? Widen.leaf + : [depth[`length`], maxDepth] extends [maxDepth, depth[`length`]] ? type + : type extends any.array ? internal.widenAll + : { -readonly [k in keyof type]: internal.widen } + + type widenAll< + xs extends any.array, + acc extends any.array, + depth extends void[], + maxDepth extends number, + > = [depth[`length`], maxDepth] extends [maxDepth, depth[`length`]] ? xs + : xs extends nonempty.array + ? internal.widen extends infer x + ? widenAll + : never + : acc +}