diff --git a/eslint.config.ts b/eslint.config.ts index 0a37e7b..fa7b48a 100644 --- a/eslint.config.ts +++ b/eslint.config.ts @@ -41,7 +41,7 @@ export default defineConfig([ "@typescript-eslint/no-unused-vars": [ "error", { - varsIgnorePattern: "^_", + varsIgnorePattern: "^_[^_]", reportUsedIgnorePattern: true, }, ], diff --git a/src/internal.ts b/src/internal.ts index 4b17fa0..58585d9 100644 --- a/src/internal.ts +++ b/src/internal.ts @@ -7,8 +7,6 @@ export interface Identity extends HKT { // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion export const never = undefined as never; -export type MaxDepth = 20; - export class AssertionError extends Error { public constructor(msg?: string) { super(msg); diff --git a/src/mixin/array.ts b/src/mixin/array.ts index e0baba6..3153a60 100644 --- a/src/mixin/array.ts +++ b/src/mixin/array.ts @@ -2,6 +2,7 @@ import { makeFluent } from "../base"; import type { HKT } from "../base/hkt"; import { Mixin, type Input, type Return } from "../base/mixin"; import { assert, assertType, type HidePrototype } from "../internal"; +import type { MaxDepth, At } from "../utility"; type IterArgs = [ element: T[number], @@ -9,11 +10,7 @@ type IterArgs = [ array: Readonly, ]; -type MaxDepth = 20; - -type Unordered = T extends unknown[] - ? T[number][] - : readonly T[number][]; +type Unordered = T[number][]; type Map< T extends readonly unknown[], @@ -38,54 +35,6 @@ type Reverse< ? TOut : T[number][]; -namespace At { - export type Dec = [ - -1, - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - ][N]; - - export type Select< - T extends readonly unknown[], - Skip extends number, - > = Skip extends 0 - ? T extends readonly [...unknown[], infer T] - ? T - : undefined - : T extends readonly [...infer TRest, unknown] - ? Select> - : undefined; -} - -type At< - T extends readonly unknown[], - K extends keyof T & number, -> = number extends T["length"] - ? T[K] | undefined - : `${K}` extends `-${infer N extends number}` - ? At.Select> - : T[K]; - type Repeat< T extends readonly unknown[], N extends number, @@ -97,38 +46,6 @@ type Repeat< ? T[number][] : Repeat; -namespace Flat { - type Inc< - N extends number, - Acc extends unknown[] = [], - > = Acc["length"] extends N - ? [...Acc, null]["length"] - : Inc; - - export type Leaves< - T, - Depth extends number = 0, - > = Depth extends MaxDepth - ? T - : T extends readonly unknown[] - ? Leaves> - : T; - - export type Rebuild< - T extends readonly unknown[], - Depth extends number = 0, - > = Depth extends MaxDepth - ? T - : T extends readonly [infer H, ...infer R] - ? H extends readonly unknown[] - ? [...Rebuild>, ...Rebuild] - : [H, ...Rebuild] - : []; -} - -export type Flat = - number extends T["length"] ? Flat.Leaves[] : Flat.Rebuild; - function isArray(v: unknown): v is readonly unknown[] { return typeof v === "object" && globalThis.Array.isArray(v); } diff --git a/src/mixin/math.ts b/src/mixin/math.ts index 3572169..d10ad57 100644 --- a/src/mixin/math.ts +++ b/src/mixin/math.ts @@ -6,21 +6,10 @@ import { type Props, type Return, } from "../base/mixin"; -import { assertType, type MaxDepth } from "../internal"; +import { assertType } from "../internal"; +import type { Inc } from "../utility"; import { applySwizzle, type Swizzle } from "./math/swizzle"; -type Vec< - T, - N extends number, - TOut extends T[] = [], -> = TOut["length"] extends N - ? TOut - : TOut["length"] extends MaxDepth - ? T[] - : Vec; - -type Inc = [...Vec, null]["length"]; - type Sign = T extends 0 ? 0 : `${T}` extends `-${number}` diff --git a/src/mixin/math/swizzle.ts b/src/mixin/math/swizzle.ts index 668d37a..e3a3595 100644 --- a/src/mixin/math/swizzle.ts +++ b/src/mixin/math/swizzle.ts @@ -1,26 +1,13 @@ import { makeFluent } from "../../base"; import type { Props, Return } from "../../base/mixin"; -import { type MaxDepth } from "../../internal"; import { Math } from "../math"; +import type { Vec, Dec, Inc as __Inc } from "../../utility"; type Axis = typeof axis; -type Vec< - T, - N extends number, - TOut extends T[] = [], -> = TOut["length"] extends N - ? TOut - : TOut["length"] extends MaxDepth - ? T[] - : Vec; type Inc = N extends number - ? [...Vec, null]["length"] + ? __Inc : 0; -type Dec = - Vec extends [...infer Rest extends null[], null] - ? Rest["length"] - : -1; type Next< N extends number, @@ -52,13 +39,8 @@ type Key< type IsAscending< State extends (number | undefined)[], Prev extends number | undefined = undefined, -> = State extends readonly [ - infer Current extends number, - ...infer Rest extends number[], -] - ? Current extends Inc - ? IsAscending> - : false +> = State extends readonly [Inc, ...infer Rest extends number[]] + ? IsAscending> : State extends readonly [] ? true : false; diff --git a/src/utility.ts b/src/utility.ts new file mode 100644 index 0000000..b514cbb --- /dev/null +++ b/src/utility.ts @@ -0,0 +1,252 @@ +export type MaxDepth = 50; + +export type Pretty = { [K in keyof T]: T[K] }; +export type Constrain = T extends U ? T : never; + +namespace Vec { + // prettier-ignore + type Lookup = N extends 0 + ? [] + : N extends 1 + ? [T] + : N extends 2 + ? [T, T] + : N extends 3 + ? [T, T, T] + : N extends 4 + ? [T, T, T, T] + : N extends 5 + ? [T, T, T, T, T] + : N extends 6 + ? [T, T, T, T, T, T] + : N extends 7 + ? [T, T, T, T, T, T, T] + : N extends 8 + ? [T, T, T, T, T, T, T, T] + : N extends 9 + ? [T, T, T, T, T, T, T, T, T] + : N extends 10 + ? [T, T, T, T, T, T, T, T, T, T] + : N extends 11 + ? [T, T, T, T, T, T, T, T, T, T, T] + : N extends 12 + ? [T, T, T, T, T, T, T, T, T, T, T, T] + : N extends 13 + ? [T, T, T, T, T, T, T, T, T, T, T, T, T] + : N extends 14 + ? [T, T, T, T, T, T, T, T, T, T, T, T, T, T] + : N extends 15 + ? [T, T, T, T, T, T, T, T, T, T, T, T, T, T, T] + : N extends 16 + ? [T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T] + : N extends 17 + ? [T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T] + : N extends 18 + ? [T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T] + : N extends 19 + ? [T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T] + : N extends 20 + ? [T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T] + : [T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T]; + + export type Recursive< + T, + N extends number, + TOut extends T[] = Lookup, + TAcc extends never[] = [], + > = TOut["length"] extends N + ? TOut + : TAcc["length"] extends MaxDepth + ? T[] + : Recursive; +} +export type Vec< + T, + N extends number, + Readonly extends boolean = false, +> = number extends N + ? Readonly extends true + ? readonly T[] + : T[] + : `${N}` extends `${infer U extends number}${`.${number}` | ""}` + ? `${N}` extends `-${number}` + ? T[] + : Vec.Recursive extends infer V + ? Readonly extends true + ? { readonly [K in keyof V]: V[K] } + : V + : never + : T[]; + +export type Inc = Constrain< + number extends N ? number : [...Vec, never]["length"], + number +>; + +export type Dec = N extends 0 + ? -1 + : `${N}` extends `-${infer U extends number}` + ? `-${Inc}` extends `${infer O extends number}` + ? O + : never + : `${N}` extends `${number}` + ? Vec extends [never, ...infer Rest] + ? Rest["length"] + : number + : number; + +namespace At { + export type FindBack< + T extends readonly unknown[], + N extends number, + TAcc extends never[] = [], + > = T extends readonly [...infer Rest, infer Last] + ? TAcc["length"] extends N + ? Last + : TAcc["length"] extends MaxDepth + ? Rest[number] + : FindBack + : T extends readonly [] + ? undefined + : never; + + export type Sequence< + T extends readonly unknown[], + N extends readonly number[], + Fallback extends T[number][], + TOut extends T[number][] = [], + > = TOut["length"] extends MaxDepth + ? Fallback + : N extends readonly [ + infer Current extends number, + ...infer Rest extends number[], + ] + ? Sequence]> + : N extends readonly [] + ? TOut + : never; +} +export type At< + T extends readonly unknown[], + N extends number | readonly number[], +> = N extends readonly number[] + ? number extends N["length"] + ? (T[number] | undefined)[] + : At.Sequence> + : N extends number + ? N extends `${number}.${number}` + ? undefined + : number extends T["length"] + ? T[number] | undefined + : `${N}` extends `-${infer U extends number}` + ? At.FindBack> + : T[N] + : never; + +namespace Flat { + export type Simple< + T, + Depth extends number = 0, + > = Depth extends MaxDepth + ? T + : T extends readonly unknown[] + ? Simple> + : T; + + export type Flatten< + T extends readonly unknown[], + TOrig extends readonly unknown[] = T, + TOut extends unknown[] = [], + > = TOut["length"] extends MaxDepth + ? Flat.Simple[] + : T extends readonly [infer Current, ...infer Rest] + ? Current extends readonly unknown[] + ? number extends Current["length"] + ? Flatten + : Flatten< + Rest, + TOrig, + [...TOut, ...Flat] + > + : Flatten + : T extends readonly [] + ? TOut + : never; + export type Flat< + T extends readonly unknown[], + TOrig extends readonly unknown[] = T, + > = number extends T["length"] + ? Flat.Simple[] + : Flat.Flatten; +} +export type Flat = Flat.Flat; + +if (import.meta.vitest) { + const { test, expectTypeOf } = import.meta.vitest; + + test("Vec", () => { + expectTypeOf>().toEqualTypeOf<[]>(); + + expectTypeOf>().toEqualTypeOf< + [number, number, number, number] + >(); + expectTypeOf>().toEqualTypeOf< + readonly [number, number, number, number] + >(); + + expectTypeOf>().toEqualTypeOf< + number[] + >(); + expectTypeOf>().toEqualTypeOf(); + }); + + test("Inc", () => { + expectTypeOf>().toEqualTypeOf<1>(); + expectTypeOf>().toEqualTypeOf<51>(); + + expectTypeOf>().toEqualTypeOf(); + expectTypeOf>().toEqualTypeOf(); + expectTypeOf>().toEqualTypeOf(); + }); + + test("Dec", () => { + expectTypeOf>().toEqualTypeOf<4>(); + expectTypeOf>().toEqualTypeOf<-1>(); + expectTypeOf>().toEqualTypeOf<-5>(); + + expectTypeOf>().toEqualTypeOf(); + expectTypeOf>().toEqualTypeOf(); + }); + + test("At", () => { + type Array = [0, 1, 2, 3, 4, 5]; + + expectTypeOf>().toEqualTypeOf(); + expectTypeOf>().toEqualTypeOf(); + + expectTypeOf>().toEqualTypeOf(); + expectTypeOf>().toEqualTypeOf(); + expectTypeOf>().toEqualTypeOf< + number | undefined + >(); + + expectTypeOf>().toEqualTypeOf<[0, 5]>(); + expectTypeOf, [0]>>().toEqualTypeOf<[0]>(); + }); + + test("Flat", () => { + type Array = [0, [1, 2], [[3]]]; + + expectTypeOf>().toEqualTypeOf<[0, 1, 2, 3]>(); + expectTypeOf>().toEqualTypeOf(); + + expectTypeOf>().toEqualTypeOf< + [0, 1, 2, 3, ...boolean[]] + >(); + + expectTypeOf>().toEqualTypeOf(); + expectTypeOf< + Flat<((number[] | number)[] | number)[]> + >().toEqualTypeOf(); + }); +}