chore: DRY the types

This commit is contained in:
2026-07-03 18:55:34 +02:00
parent a57b11b451
commit 045eae553d
6 changed files with 261 additions and 123 deletions
+1 -1
View File
@@ -41,7 +41,7 @@ export default defineConfig([
"@typescript-eslint/no-unused-vars": [ "@typescript-eslint/no-unused-vars": [
"error", "error",
{ {
varsIgnorePattern: "^_", varsIgnorePattern: "^_[^_]",
reportUsedIgnorePattern: true, reportUsedIgnorePattern: true,
}, },
], ],
-2
View File
@@ -7,8 +7,6 @@ export interface Identity extends HKT {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
export const never = undefined as never; export const never = undefined as never;
export type MaxDepth = 20;
export class AssertionError extends Error { export class AssertionError extends Error {
public constructor(msg?: string) { public constructor(msg?: string) {
super(msg); super(msg);
+2 -85
View File
@@ -2,6 +2,7 @@ import { makeFluent } from "../base";
import type { HKT } from "../base/hkt"; import type { HKT } from "../base/hkt";
import { Mixin, type Input, type Return } from "../base/mixin"; import { Mixin, type Input, type Return } from "../base/mixin";
import { assert, assertType, type HidePrototype } from "../internal"; import { assert, assertType, type HidePrototype } from "../internal";
import type { MaxDepth, At } from "../utility";
type IterArgs<T extends readonly unknown[] = unknown[]> = [ type IterArgs<T extends readonly unknown[] = unknown[]> = [
element: T[number], element: T[number],
@@ -9,11 +10,7 @@ type IterArgs<T extends readonly unknown[] = unknown[]> = [
array: Readonly<T>, array: Readonly<T>,
]; ];
type MaxDepth = 20; type Unordered<T extends readonly unknown[]> = T[number][];
type Unordered<T extends readonly unknown[]> = T extends unknown[]
? T[number][]
: readonly T[number][];
type Map< type Map<
T extends readonly unknown[], T extends readonly unknown[],
@@ -38,54 +35,6 @@ type Reverse<
? TOut ? TOut
: T[number][]; : T[number][];
namespace At {
export type Dec<N extends number> = [
-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<TRest, Dec<Skip>>
: 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, At.Dec<N>>
: T[K];
type Repeat< type Repeat<
T extends readonly unknown[], T extends readonly unknown[],
N extends number, N extends number,
@@ -97,38 +46,6 @@ type Repeat<
? T[number][] ? T[number][]
: Repeat<T, N, [...Acc, null], [...TOut, ...T]>; : Repeat<T, N, [...Acc, null], [...TOut, ...T]>;
namespace Flat {
type Inc<
N extends number,
Acc extends unknown[] = [],
> = Acc["length"] extends N
? [...Acc, null]["length"]
: Inc<N, [...Acc, null]>;
export type Leaves<
T,
Depth extends number = 0,
> = Depth extends MaxDepth
? T
: T extends readonly unknown[]
? Leaves<T[number], Inc<Depth>>
: 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<H, Inc<Depth>>, ...Rebuild<R, Depth>]
: [H, ...Rebuild<R, Depth>]
: [];
}
export type Flat<T extends readonly unknown[]> =
number extends T["length"] ? Flat.Leaves<T>[] : Flat.Rebuild<T>;
function isArray(v: unknown): v is readonly unknown[] { function isArray(v: unknown): v is readonly unknown[] {
return typeof v === "object" && globalThis.Array.isArray(v); return typeof v === "object" && globalThis.Array.isArray(v);
} }
+2 -13
View File
@@ -6,21 +6,10 @@ import {
type Props, type Props,
type Return, type Return,
} from "../base/mixin"; } 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"; 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<T, N, [...TOut, T]>;
type Inc<N extends number> = [...Vec<null, N>, null]["length"];
type Sign<T extends number> = T extends 0 type Sign<T extends number> = T extends 0
? 0 ? 0
: `${T}` extends `-${number}` : `${T}` extends `-${number}`
+4 -22
View File
@@ -1,26 +1,13 @@
import { makeFluent } from "../../base"; import { makeFluent } from "../../base";
import type { Props, Return } from "../../base/mixin"; import type { Props, Return } from "../../base/mixin";
import { type MaxDepth } from "../../internal";
import { Math } from "../math"; import { Math } from "../math";
import type { Vec, Dec, Inc as __Inc } from "../../utility";
type Axis = typeof axis; type Axis = typeof axis;
type Vec<
T,
N extends number,
TOut extends T[] = [],
> = TOut["length"] extends N
? TOut
: TOut["length"] extends MaxDepth
? T[]
: Vec<T, N, [...TOut, T]>;
type Inc<N extends number | undefined> = N extends number type Inc<N extends number | undefined> = N extends number
? [...Vec<null, N>, null]["length"] ? __Inc<N>
: 0; : 0;
type Dec<N extends number> =
Vec<null, N> extends [...infer Rest extends null[], null]
? Rest["length"]
: -1;
type Next< type Next<
N extends number, N extends number,
@@ -52,13 +39,8 @@ type Key<
type IsAscending< type IsAscending<
State extends (number | undefined)[], State extends (number | undefined)[],
Prev extends number | undefined = undefined, Prev extends number | undefined = undefined,
> = State extends readonly [ > = State extends readonly [Inc<Prev>, ...infer Rest extends number[]]
infer Current extends number, ? IsAscending<Rest, Inc<Prev>>
...infer Rest extends number[],
]
? Current extends Inc<Prev>
? IsAscending<Rest, Inc<Prev>>
: false
: State extends readonly [] : State extends readonly []
? true ? true
: false; : false;
+252
View File
@@ -0,0 +1,252 @@
export type MaxDepth = 50;
export type Pretty<T> = { [K in keyof T]: T[K] };
export type Constrain<T, U> = T extends U ? T : never;
namespace Vec {
// prettier-ignore
type Lookup<T, N extends number> = 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<T, N>,
TAcc extends never[] = [],
> = TOut["length"] extends N
? TOut
: TAcc["length"] extends MaxDepth
? T[]
: Recursive<T, N, [...TOut, T], [...TAcc, never]>;
}
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<T, U> extends infer V
? Readonly extends true
? { readonly [K in keyof V]: V[K] }
: V
: never
: T[];
export type Inc<N extends number> = Constrain<
number extends N ? number : [...Vec<never, N>, never]["length"],
number
>;
export type Dec<N extends number> = N extends 0
? -1
: `${N}` extends `-${infer U extends number}`
? `-${Inc<U>}` extends `${infer O extends number}`
? O
: never
: `${N}` extends `${number}`
? Vec<never, N> 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<Rest, N, [...TAcc, never]>
: 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<T, Rest, Fallback, [...TOut, At<T, Current>]>
: 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<T, N, Vec<T[number], N["length"]>>
: N extends number
? N extends `${number}.${number}`
? undefined
: number extends T["length"]
? T[number] | undefined
: `${N}` extends `-${infer U extends number}`
? At.FindBack<T, Dec<U>>
: T[N]
: never;
namespace Flat {
export type Simple<
T,
Depth extends number = 0,
> = Depth extends MaxDepth
? T
: T extends readonly unknown[]
? Simple<T[number], Inc<Depth>>
: T;
export type Flatten<
T extends readonly unknown[],
TOrig extends readonly unknown[] = T,
TOut extends unknown[] = [],
> = TOut["length"] extends MaxDepth
? Flat.Simple<TOrig>[]
: T extends readonly [infer Current, ...infer Rest]
? Current extends readonly unknown[]
? number extends Current["length"]
? Flatten<Rest, TOrig, [...TOut, ...Current]>
: Flatten<
Rest,
TOrig,
[...TOut, ...Flat<Current>]
>
: Flatten<Rest, TOrig, [...TOut, Current]>
: T extends readonly []
? TOut
: never;
export type Flat<
T extends readonly unknown[],
TOrig extends readonly unknown[] = T,
> = number extends T["length"]
? Flat.Simple<T>[]
: Flat.Flatten<T, TOrig>;
}
export type Flat<T extends readonly unknown[]> = Flat.Flat<T>;
if (import.meta.vitest) {
const { test, expectTypeOf } = import.meta.vitest;
test("Vec<T, N>", () => {
expectTypeOf<Vec<never, 0>>().toEqualTypeOf<[]>();
expectTypeOf<Vec<number, 4>>().toEqualTypeOf<
[number, number, number, number]
>();
expectTypeOf<Vec<number, 4, true>>().toEqualTypeOf<
readonly [number, number, number, number]
>();
expectTypeOf<Vec<number, typeof Infinity>>().toEqualTypeOf<
number[]
>();
expectTypeOf<Vec<number, number>>().toEqualTypeOf<number[]>();
});
test("Inc<N>", () => {
expectTypeOf<Inc<0>>().toEqualTypeOf<1>();
expectTypeOf<Inc<50>>().toEqualTypeOf<51>();
expectTypeOf<Inc<-5>>().toEqualTypeOf<number>();
expectTypeOf<Inc<typeof Infinity>>().toEqualTypeOf<number>();
expectTypeOf<Inc<number>>().toEqualTypeOf<number>();
});
test("Dec<N>", () => {
expectTypeOf<Dec<5>>().toEqualTypeOf<4>();
expectTypeOf<Dec<0>>().toEqualTypeOf<-1>();
expectTypeOf<Dec<-4>>().toEqualTypeOf<-5>();
expectTypeOf<Dec<typeof Infinity>>().toEqualTypeOf<number>();
expectTypeOf<Dec<number>>().toEqualTypeOf<number>();
});
test("At<T, N>", () => {
type Array = [0, 1, 2, 3, 4, 5];
expectTypeOf<At<Array, 0>>().toEqualTypeOf<Array[0]>();
expectTypeOf<At<Array, -1>>().toEqualTypeOf<Array[5]>();
expectTypeOf<At<Array, 10>>().toEqualTypeOf<undefined>();
expectTypeOf<At<Array, -10>>().toEqualTypeOf<undefined>();
expectTypeOf<At<number[], 0>>().toEqualTypeOf<
number | undefined
>();
expectTypeOf<At<Array, [0, -1]>>().toEqualTypeOf<[0, 5]>();
expectTypeOf<At<Readonly<Array>, [0]>>().toEqualTypeOf<[0]>();
});
test("Flat<T>", () => {
type Array = [0, [1, 2], [[3]]];
expectTypeOf<Flat<Array>>().toEqualTypeOf<[0, 1, 2, 3]>();
expectTypeOf<Flat<number[]>>().toEqualTypeOf<number[]>();
expectTypeOf<Flat<[...Array, boolean[]]>>().toEqualTypeOf<
[0, 1, 2, 3, ...boolean[]]
>();
expectTypeOf<Flat<number[][][]>>().toEqualTypeOf<number[]>();
expectTypeOf<
Flat<((number[] | number)[] | number)[]>
>().toEqualTypeOf<number[]>();
});
}