feat: more math & refactor
This commit is contained in:
@@ -46,6 +46,7 @@ export default defineConfig([
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
"@typescript-eslint/no-namespace": "off",
|
"@typescript-eslint/no-namespace": "off",
|
||||||
|
"@typescript-eslint/no-empty-object-type": "off",
|
||||||
},
|
},
|
||||||
languageOptions: {
|
languageOptions: {
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
|
|||||||
+1
-1
@@ -21,7 +21,7 @@
|
|||||||
"jiti": "^2.7.0",
|
"jiti": "^2.7.0",
|
||||||
"madge": "^8.0.0",
|
"madge": "^8.0.0",
|
||||||
"prettier": "^3.8.4",
|
"prettier": "^3.8.4",
|
||||||
"typescript": "~6.0.2",
|
"typescript": "6",
|
||||||
"typescript-eslint": "^8.59.2",
|
"typescript-eslint": "^8.59.2",
|
||||||
"vitest": "^4.1.9"
|
"vitest": "^4.1.9"
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+1
-1
@@ -39,7 +39,7 @@ importers:
|
|||||||
specifier: ^3.8.4
|
specifier: ^3.8.4
|
||||||
version: 3.8.4
|
version: 3.8.4
|
||||||
typescript:
|
typescript:
|
||||||
specifier: ~6.0.2
|
specifier: '6'
|
||||||
version: 6.0.3
|
version: 6.0.3
|
||||||
typescript-eslint:
|
typescript-eslint:
|
||||||
specifier: ^8.59.2
|
specifier: ^8.59.2
|
||||||
|
|||||||
+4
-3
@@ -1,18 +1,19 @@
|
|||||||
export interface HKT<In = unknown, Out = unknown> {
|
export interface HKT<In = unknown, Out = unknown> {
|
||||||
readonly _meta: In;
|
readonly _in: In;
|
||||||
|
readonly _out: Out;
|
||||||
readonly _t: unknown;
|
readonly _t: unknown;
|
||||||
new: (t: never) => Out;
|
new: (t: never) => Out;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Param<
|
type Param<
|
||||||
This extends HKT,
|
This extends HKT,
|
||||||
U = This["_meta"],
|
U = This["_in"],
|
||||||
> = This["_t"] extends infer T ? (T extends U ? T : U) : never;
|
> = This["_t"] extends infer T ? (T extends U ? T : U) : never;
|
||||||
|
|
||||||
export namespace HKT {
|
export namespace HKT {
|
||||||
export type T<
|
export type T<
|
||||||
This extends HKT,
|
This extends HKT,
|
||||||
T = This extends { _meta: infer I } ? I : unknown,
|
T = This["_in"], //This extends { _in: infer I } ? I : unknown,
|
||||||
> = Param<This, T>;
|
> = Param<This, T>;
|
||||||
export type Apply<T extends HKT, t extends T["_t"]> = ReturnType<
|
export type Apply<T extends HKT, t extends T["_t"]> = ReturnType<
|
||||||
(T & { _t: t })["new"]
|
(T & { _t: t })["new"]
|
||||||
|
|||||||
+1
-2
@@ -33,8 +33,7 @@ export function makeFluent<const Reg extends Registry>(
|
|||||||
const f = { value } as unknown as Fluent<T, Reg>;
|
const f = { value } as unknown as Fluent<T, Reg>;
|
||||||
|
|
||||||
for (const mixin of registry) {
|
for (const mixin of registry) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
mixin.fn(value, f, fluent);
|
||||||
mixin.fn(value, f, fluent as (value: unknown) => never);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return f;
|
return f;
|
||||||
|
|||||||
+26
-10
@@ -1,6 +1,7 @@
|
|||||||
import type { Fluent } from ".";
|
import type { Fluent } from ".";
|
||||||
import { never } from "../internal";
|
import { never } from "../internal";
|
||||||
import type { Registry } from "../registry";
|
import type { Registry } from "../registry";
|
||||||
|
import type { RecPartial } from "../utility";
|
||||||
import type { HKT } from "./hkt";
|
import type { HKT } from "./hkt";
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
@@ -8,28 +9,43 @@ export interface Props {
|
|||||||
readonly meta: { registry: Registry };
|
readonly meta: { registry: Registry };
|
||||||
}
|
}
|
||||||
|
|
||||||
type MixinHKT = HKT<Props>;
|
type MixinHKT<I> = HKT<Props, RecPartial<I>>;
|
||||||
type MixinFn = (
|
type MixinFn<I> = <T>(
|
||||||
value: unknown,
|
value: T,
|
||||||
fluent: Record<PropertyKey, unknown>,
|
fluent: object & RecPartial<I>,
|
||||||
callback: (value: unknown) => never,
|
callback: <U>(value: U) => Fluent<U, Registry>,
|
||||||
) => void;
|
) => void;
|
||||||
|
|
||||||
export interface Mixin<T extends MixinHKT = MixinHKT> {
|
export interface Mixin<
|
||||||
|
I = unknown,
|
||||||
|
T extends MixinHKT<I> = MixinHKT<I>,
|
||||||
|
> {
|
||||||
|
interface: I;
|
||||||
hkt: T;
|
hkt: T;
|
||||||
fn: MixinFn;
|
fn: MixinFn<I>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Mixin<T extends MixinHKT>(fn: MixinFn): Mixin<T> {
|
export function Mixin<I, T extends MixinHKT<I>>(
|
||||||
|
fn: MixinFn<I>,
|
||||||
|
): Mixin<I, T> {
|
||||||
return {
|
return {
|
||||||
|
interface: never,
|
||||||
hkt: never,
|
hkt: never,
|
||||||
fn,
|
fn,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export namespace Mixin {
|
export namespace Mixin {
|
||||||
export type HKT = MixinHKT;
|
export type HKT<I> = MixinHKT<I>;
|
||||||
export type Function = MixinFn;
|
export function partial<I, T>(
|
||||||
|
callback: (
|
||||||
|
value: T,
|
||||||
|
fluent: object & RecPartial<I>,
|
||||||
|
callback: <P>(value: P) => Fluent<P, Registry>,
|
||||||
|
) => void,
|
||||||
|
): typeof callback {
|
||||||
|
return callback;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export declare const shim: unique symbol;
|
export declare const shim: unique symbol;
|
||||||
|
|||||||
+8
-2
@@ -7,6 +7,10 @@ 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 function isArray(v: unknown): v is unknown[] {
|
||||||
|
return typeof v === "object" && v !== null && Array.isArray(v);
|
||||||
|
}
|
||||||
|
|
||||||
export class AssertionError extends Error {
|
export class AssertionError extends Error {
|
||||||
public constructor(msg?: string) {
|
public constructor(msg?: string) {
|
||||||
super(msg);
|
super(msg);
|
||||||
@@ -26,9 +30,11 @@ export function assert(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters, @typescript-eslint/no-unused-vars
|
export namespace assert {
|
||||||
export function assertType<T>(_v: unknown): asserts _v is T {
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
export function type<T>(_v: unknown): asserts _v is NoInfer<T> {
|
||||||
/* empty */
|
/* empty */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||||
|
|||||||
+84
-88
@@ -1,7 +1,12 @@
|
|||||||
import { makeFluent } from "../base";
|
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 {
|
||||||
import { assert, assertType, type HidePrototype } from "../internal";
|
Mixin,
|
||||||
|
type Input,
|
||||||
|
type Props,
|
||||||
|
type Return,
|
||||||
|
} from "../base/mixin";
|
||||||
|
import { assert, type HidePrototype } from "../internal";
|
||||||
import type { MaxDepth, At } from "../utility";
|
import type { MaxDepth, At } from "../utility";
|
||||||
|
|
||||||
type IterArgs<T extends readonly unknown[] = unknown[]> = [
|
type IterArgs<T extends readonly unknown[] = unknown[]> = [
|
||||||
@@ -50,10 +55,10 @@ function isArray(v: unknown): v is readonly unknown[] {
|
|||||||
return typeof v === "object" && globalThis.Array.isArray(v);
|
return typeof v === "object" && globalThis.Array.isArray(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Array extends Mixin.HKT {
|
interface IArray<
|
||||||
new: (t: HKT.T<this>) => Input<typeof t> extends infer T
|
T extends readonly unknown[] = readonly unknown[],
|
||||||
? T extends readonly (infer Item)[]
|
t extends Props = Props,
|
||||||
? {
|
> {
|
||||||
/**
|
/**
|
||||||
* Index the array using the specified zero-based index.
|
* Index the array using the specified zero-based index.
|
||||||
* Negative indexes start at the end of the array.
|
* Negative indexes start at the end of the array.
|
||||||
@@ -78,7 +83,7 @@ export interface Array extends Mixin.HKT {
|
|||||||
at: <const K extends keyof T & number>(
|
at: <const K extends keyof T & number>(
|
||||||
/** Zero-based index */
|
/** Zero-based index */
|
||||||
index: K,
|
index: K,
|
||||||
) => Return<At<T, K>, typeof t>;
|
) => Return<At<T, K>, t>;
|
||||||
/**
|
/**
|
||||||
* Interospect each item of the array using the specified
|
* Interospect each item of the array using the specified
|
||||||
* callback
|
* callback
|
||||||
@@ -103,7 +108,7 @@ export interface Array extends Mixin.HKT {
|
|||||||
* @param array The array being iterated
|
* @param array The array being iterated
|
||||||
*/
|
*/
|
||||||
callback: (...args: IterArgs<T>) => void,
|
callback: (...args: IterArgs<T>) => void,
|
||||||
) => Return<T, typeof t>;
|
) => Return<T, t>;
|
||||||
/**
|
/**
|
||||||
* Transform each item of the array using the specified callback
|
* Transform each item of the array using the specified callback
|
||||||
* @param callback Predicate to compute each new value with
|
* @param callback Predicate to compute each new value with
|
||||||
@@ -125,7 +130,7 @@ export interface Array extends Mixin.HKT {
|
|||||||
* @return The new value to use for this element
|
* @return The new value to use for this element
|
||||||
*/
|
*/
|
||||||
callback: (...args: IterArgs<T>) => U,
|
callback: (...args: IterArgs<T>) => U,
|
||||||
) => Return<Map<T, U>, typeof t>;
|
) => Return<Map<T, U>, t>;
|
||||||
/**
|
/**
|
||||||
* Extend the array by repeating its current contents n times
|
* Extend the array by repeating its current contents n times
|
||||||
* @param count The amount of repetitions to extend the array by
|
* @param count The amount of repetitions to extend the array by
|
||||||
@@ -141,7 +146,7 @@ export interface Array extends Mixin.HKT {
|
|||||||
extend: <const N extends number>(
|
extend: <const N extends number>(
|
||||||
/** The amount of repetitions to extend the array by */
|
/** The amount of repetitions to extend the array by */
|
||||||
count: N,
|
count: N,
|
||||||
) => Return<Repeat<T, N>, typeof t>;
|
) => Return<Repeat<T, N>, t>;
|
||||||
/**
|
/**
|
||||||
* Filter the array to contain only items satisfying the
|
* Filter the array to contain only items satisfying the
|
||||||
* specified callback conditional
|
* specified callback conditional
|
||||||
@@ -164,7 +169,7 @@ export interface Array extends Mixin.HKT {
|
|||||||
* @return `true` if the value should be kept and `false` if it should not
|
* @return `true` if the value should be kept and `false` if it should not
|
||||||
*/
|
*/
|
||||||
callback: (...args: IterArgs<T>) => boolean,
|
callback: (...args: IterArgs<T>) => boolean,
|
||||||
) => Return<Unordered<T>, typeof t>) & {
|
) => Return<Unordered<T>, t>) & {
|
||||||
/**
|
/**
|
||||||
* Filter the array to only contain items which are not
|
* Filter the array to only contain items which are not
|
||||||
* `null` or `undefined`.
|
* `null` or `undefined`.
|
||||||
@@ -183,15 +188,9 @@ export interface Array extends Mixin.HKT {
|
|||||||
*/
|
*/
|
||||||
some: () => Return<
|
some: () => Return<
|
||||||
T extends unknown[]
|
T extends unknown[]
|
||||||
? Exclude<
|
? Exclude<T[number], null | undefined>[]
|
||||||
T[number],
|
: readonly Exclude<T[number], null | undefined>[],
|
||||||
null | undefined
|
t
|
||||||
>[]
|
|
||||||
: readonly Exclude<
|
|
||||||
T[number],
|
|
||||||
null | undefined
|
|
||||||
>[],
|
|
||||||
typeof t
|
|
||||||
>;
|
>;
|
||||||
} & HidePrototype;
|
} & HidePrototype;
|
||||||
/**
|
/**
|
||||||
@@ -221,11 +220,8 @@ export interface Array extends Mixin.HKT {
|
|||||||
* @param index The index of the current element in the array
|
* @param index The index of the current element in the array
|
||||||
* @param array The array being iterated
|
* @param array The array being iterated
|
||||||
*/
|
*/
|
||||||
callback: (
|
callback: (value: U, ...args: IterArgs<T>) => U,
|
||||||
value: U,
|
) => Return<U, t>) & {
|
||||||
...args: IterArgs<T>
|
|
||||||
) => U,
|
|
||||||
) => Return<U, typeof t>) & {
|
|
||||||
/**
|
/**
|
||||||
* Collapse the array using the specified accumulator function, starting from the right.
|
* Collapse the array using the specified accumulator function, starting from the right.
|
||||||
* @param initial The initial value of the accumulator
|
* @param initial The initial value of the accumulator
|
||||||
@@ -253,11 +249,8 @@ export interface Array extends Mixin.HKT {
|
|||||||
* @param index The index of the current element in the array
|
* @param index The index of the current element in the array
|
||||||
* @param array The array being iterated
|
* @param array The array being iterated
|
||||||
*/
|
*/
|
||||||
callback: (
|
callback: (value: U, ...args: IterArgs<T>) => U,
|
||||||
value: U,
|
) => Return<U, t>;
|
||||||
...args: IterArgs<T>
|
|
||||||
) => U,
|
|
||||||
) => Return<U, typeof t>;
|
|
||||||
} & HidePrototype;
|
} & HidePrototype;
|
||||||
/**
|
/**
|
||||||
* Get the number of items in the array
|
* Get the number of items in the array
|
||||||
@@ -269,7 +262,7 @@ export interface Array extends Mixin.HKT {
|
|||||||
* expect(count).toBe(3);
|
* expect(count).toBe(3);
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
length: () => Return<T["length"], typeof t>;
|
length: () => Return<T["length"], t>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rearrange the items in the array in accordance to the
|
* Rearrange the items in the array in accordance to the
|
||||||
@@ -299,12 +292,12 @@ export interface Array extends Mixin.HKT {
|
|||||||
* and `0` if `a` and `b` are considered the same.
|
* and `0` if `a` and `b` are considered the same.
|
||||||
*/
|
*/
|
||||||
callback: (
|
callback: (
|
||||||
a: Item,
|
a: T[number],
|
||||||
b: Item,
|
b: T[number],
|
||||||
arr: Readonly<T>,
|
arr: Readonly<T>,
|
||||||
) => number,
|
) => number,
|
||||||
) => Return<Unordered<T>, typeof t>) &
|
) => Return<Unordered<T>, t>) &
|
||||||
([Item] extends [string]
|
([T[number]] extends [string]
|
||||||
? {
|
? {
|
||||||
/**
|
/**
|
||||||
* @param via A callback to compute a
|
* @param via A callback to compute a
|
||||||
@@ -316,11 +309,8 @@ export interface Array extends Mixin.HKT {
|
|||||||
* @param v The current element
|
* @param v The current element
|
||||||
* @return A string representation of `v`
|
* @return A string representation of `v`
|
||||||
*/
|
*/
|
||||||
via?: (v: Item) => string,
|
via?: (v: T[number]) => string,
|
||||||
) => Return<
|
) => Return<Unordered<T>, t>;
|
||||||
Unordered<T>,
|
|
||||||
typeof t
|
|
||||||
>;
|
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
/**
|
/**
|
||||||
@@ -333,36 +323,21 @@ export interface Array extends Mixin.HKT {
|
|||||||
* @param v The current element
|
* @param v The current element
|
||||||
* @return The string representation of `v`
|
* @return The string representation of `v`
|
||||||
*/
|
*/
|
||||||
via: (v: Item) => string,
|
via: (v: T[number]) => string,
|
||||||
) => Return<
|
) => Return<Unordered<T>, t>;
|
||||||
Unordered<T>,
|
|
||||||
typeof t
|
|
||||||
>;
|
|
||||||
}) &
|
}) &
|
||||||
([Item] extends [number]
|
([T[number]] extends [number]
|
||||||
? {
|
? {
|
||||||
ascending: () => Return<
|
ascending: () => Return<Unordered<T>, t>;
|
||||||
Unordered<T>,
|
descending: () => Return<Unordered<T>, t>;
|
||||||
typeof t
|
|
||||||
>;
|
|
||||||
descending: () => Return<
|
|
||||||
Unordered<T>,
|
|
||||||
typeof t
|
|
||||||
>;
|
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
ascending: (
|
ascending: (
|
||||||
via: (v: Item) => number,
|
via: (v: T[number]) => number,
|
||||||
) => Return<
|
) => Return<Unordered<T>, t>;
|
||||||
Unordered<T>,
|
|
||||||
typeof t
|
|
||||||
>;
|
|
||||||
descending: (
|
descending: (
|
||||||
via: (v: Item) => number,
|
via: (v: T[number]) => number,
|
||||||
) => Return<
|
) => Return<Unordered<T>, t>;
|
||||||
Unordered<T>,
|
|
||||||
typeof t
|
|
||||||
>;
|
|
||||||
}) & {
|
}) & {
|
||||||
/**
|
/**
|
||||||
* Sort alphabetically (A-Z) by Unicode code points
|
* Sort alphabetically (A-Z) by Unicode code points
|
||||||
@@ -376,12 +351,19 @@ export interface Array extends Mixin.HKT {
|
|||||||
*/
|
*/
|
||||||
alpha: unknown;
|
alpha: unknown;
|
||||||
} & HidePrototype;
|
} & HidePrototype;
|
||||||
reverse: () => Return<Reverse<T>, typeof t>;
|
reverse: () => Return<Reverse<T>, t>;
|
||||||
}
|
}
|
||||||
: unknown
|
|
||||||
|
export interface Array extends Mixin.HKT<IArray> {
|
||||||
|
new: (
|
||||||
|
t: HKT.T<this>,
|
||||||
|
) => Input<typeof t> extends infer T
|
||||||
|
? T extends readonly unknown[]
|
||||||
|
? IArray<T, typeof t>
|
||||||
|
: {}
|
||||||
: never;
|
: never;
|
||||||
}
|
}
|
||||||
export const Array = Mixin<Array>((value, $, fluent) => {
|
export const Array = Mixin<IArray, Array>((value, $, fluent) => {
|
||||||
if (!isArray(value)) return;
|
if (!isArray(value)) return;
|
||||||
|
|
||||||
$.at = (index: number) => {
|
$.at = (index: number) => {
|
||||||
@@ -407,39 +389,47 @@ export const Array = Mixin<Array>((value, $, fluent) => {
|
|||||||
return fluent(value.map(callback));
|
return fluent(value.map(callback));
|
||||||
};
|
};
|
||||||
|
|
||||||
$.filter = (callback: (...args: IterArgs) => boolean) => {
|
$.filter = Object.assign(
|
||||||
|
(callback: (...args: IterArgs) => boolean) => {
|
||||||
return fluent(value.filter(callback));
|
return fluent(value.filter(callback));
|
||||||
};
|
},
|
||||||
assertType<object>($.filter);
|
{
|
||||||
Object.assign($.filter, {
|
|
||||||
some: () => {
|
some: () => {
|
||||||
return fluent(
|
return fluent(
|
||||||
value.filter((v) => v !== null && v !== undefined),
|
value.filter(
|
||||||
|
(v) => v !== null && v !== undefined,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
$.reduce = (
|
$.reduce = Object.assign(
|
||||||
|
(
|
||||||
initial: unknown,
|
initial: unknown,
|
||||||
callback: (value: unknown, ...args: IterArgs) => unknown,
|
callback: (value: unknown, ...args: IterArgs) => unknown,
|
||||||
) => {
|
) => {
|
||||||
return fluent(value.reduce(callback, initial));
|
return fluent(value.reduce(callback, initial));
|
||||||
};
|
},
|
||||||
assertType<object>($.reduce);
|
{
|
||||||
Object.assign($.reduce, {
|
|
||||||
right: (
|
right: (
|
||||||
initial: unknown,
|
initial: unknown,
|
||||||
callback: (value: unknown, ...args: IterArgs) => unknown,
|
callback: (
|
||||||
|
value: unknown,
|
||||||
|
...args: IterArgs
|
||||||
|
) => unknown,
|
||||||
) => {
|
) => {
|
||||||
return fluent(value.reduceRight(callback, initial));
|
return fluent(value.reduceRight(callback, initial));
|
||||||
},
|
},
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
$.length = () => {
|
$.length = () => {
|
||||||
return fluent(value.length);
|
return fluent(value.length);
|
||||||
};
|
};
|
||||||
|
|
||||||
$.sort = (
|
$.sort = Object.assign(
|
||||||
|
(
|
||||||
callback: (
|
callback: (
|
||||||
a: unknown,
|
a: unknown,
|
||||||
b: unknown,
|
b: unknown,
|
||||||
@@ -449,9 +439,8 @@ export const Array = Mixin<Array>((value, $, fluent) => {
|
|||||||
return fluent(
|
return fluent(
|
||||||
value.toSorted((a, b) => callback(a, b, value)),
|
value.toSorted((a, b) => callback(a, b, value)),
|
||||||
);
|
);
|
||||||
};
|
},
|
||||||
assertType<object>($.sort);
|
{
|
||||||
Object.assign($.sort, {
|
|
||||||
alpha: (
|
alpha: (
|
||||||
via: (v: unknown) => string = (v) => (
|
via: (v: unknown) => string = (v) => (
|
||||||
assert(typeof v === "string"),
|
assert(typeof v === "string"),
|
||||||
@@ -459,7 +448,9 @@ export const Array = Mixin<Array>((value, $, fluent) => {
|
|||||||
),
|
),
|
||||||
) => {
|
) => {
|
||||||
return fluent(
|
return fluent(
|
||||||
value.toSorted((a, b) => (via(a) < via(b) ? -1 : 1)),
|
value.toSorted((a, b) =>
|
||||||
|
via(a) < via(b) ? -1 : 1,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
ascending: (
|
ascending: (
|
||||||
@@ -468,7 +459,9 @@ export const Array = Mixin<Array>((value, $, fluent) => {
|
|||||||
v
|
v
|
||||||
),
|
),
|
||||||
) => {
|
) => {
|
||||||
return fluent(value.toSorted((a, b) => via(a) - via(b)));
|
return fluent(
|
||||||
|
value.toSorted((a, b) => via(a) - via(b)),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
descending: (
|
descending: (
|
||||||
via: (v: unknown) => number = (v) => (
|
via: (v: unknown) => number = (v) => (
|
||||||
@@ -476,9 +469,12 @@ export const Array = Mixin<Array>((value, $, fluent) => {
|
|||||||
v
|
v
|
||||||
),
|
),
|
||||||
) => {
|
) => {
|
||||||
return fluent(value.toSorted((a, b) => via(b) - via(a)));
|
return fluent(
|
||||||
|
value.toSorted((a, b) => via(b) - via(a)),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
$.reverse = () => {
|
$.reverse = () => {
|
||||||
return fluent(value.toReversed());
|
return fluent(value.toReversed());
|
||||||
|
|||||||
+34
-23
@@ -1,7 +1,13 @@
|
|||||||
import { makeFluent } from "../base";
|
import { makeFluent } from "../base";
|
||||||
import type { HKT } from "../base/hkt";
|
import type { HKT } from "../base/hkt";
|
||||||
import { Mixin, shim, type Input, type Return } from "../base/mixin";
|
import {
|
||||||
import { assert, assertType, never } from "../internal";
|
Mixin,
|
||||||
|
shim,
|
||||||
|
type Input,
|
||||||
|
type Props,
|
||||||
|
type Return,
|
||||||
|
} from "../base/mixin";
|
||||||
|
import { assert, never } from "../internal";
|
||||||
import { Base } from "./base";
|
import { Base } from "./base";
|
||||||
|
|
||||||
interface AwaitedIdentitity extends HKT {
|
interface AwaitedIdentitity extends HKT {
|
||||||
@@ -23,20 +29,23 @@ class Awaited<T extends Promise<unknown>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AsyncMixin extends Mixin.HKT {
|
interface IAsync<
|
||||||
new: (t: HKT.T<this>) => Input<typeof t> extends infer T
|
T extends Promise<unknown> = Promise<unknown>,
|
||||||
? T extends Promise<unknown>
|
t extends Props = Props,
|
||||||
? {
|
> {
|
||||||
readonly awaited: Return<Awaited<T>, typeof t>;
|
readonly awaited: Return<Awaited<T>, t>;
|
||||||
then: <U>(
|
then: <U>(
|
||||||
callback: (
|
fn: (value: T extends Promise<infer O> ? O : never) => U,
|
||||||
value: T extends Promise<infer T>
|
) => Return<Promise<U>, t>;
|
||||||
? T
|
}
|
||||||
: never,
|
|
||||||
) => U,
|
export interface AsyncMixin extends Mixin.HKT<IAsync> {
|
||||||
) => Return<Promise<U>, typeof t>;
|
new: (
|
||||||
}
|
t: HKT.T<this>,
|
||||||
: unknown
|
) => Input<typeof t> extends infer T
|
||||||
|
? T extends Promise<unknown>
|
||||||
|
? IAsync<T, typeof t>
|
||||||
|
: {}
|
||||||
: never;
|
: never;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,7 +54,8 @@ interface BaseFluent<T> {
|
|||||||
[K: PropertyKey]: unknown;
|
[K: PropertyKey]: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AsyncMixin = Mixin<AsyncMixin>((value, $, fluent) => {
|
export const AsyncMixin = Mixin<IAsync, AsyncMixin>(
|
||||||
|
(value, $, fluent) => {
|
||||||
if (!(value instanceof Promise)) return;
|
if (!(value instanceof Promise)) return;
|
||||||
|
|
||||||
$.then = (callback: (value: unknown) => unknown) => {
|
$.then = (callback: (value: unknown) => unknown) => {
|
||||||
@@ -55,8 +65,8 @@ export const AsyncMixin = Mixin<AsyncMixin>((value, $, fluent) => {
|
|||||||
Object.defineProperty($, "awaited", {
|
Object.defineProperty($, "awaited", {
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
get() {
|
get() {
|
||||||
let v: Promise<BaseFluent<unknown>> = value.then((v) =>
|
let v: Promise<BaseFluent<unknown>> = value.then(
|
||||||
fluent(v),
|
(v) => fluent(v),
|
||||||
);
|
);
|
||||||
|
|
||||||
const path: PropertyKey[] = [];
|
const path: PropertyKey[] = [];
|
||||||
@@ -77,15 +87,15 @@ export const AsyncMixin = Mixin<AsyncMixin>((value, $, fluent) => {
|
|||||||
obj !== null &&
|
obj !== null &&
|
||||||
node in obj,
|
node in obj,
|
||||||
);
|
);
|
||||||
assertType<Record<PropertyKey, unknown>>(
|
assert.type<
|
||||||
obj,
|
Record<PropertyKey, unknown>
|
||||||
);
|
>(obj);
|
||||||
|
|
||||||
obj = obj[node];
|
obj = obj[node];
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(typeof obj === "function");
|
assert(typeof obj === "function");
|
||||||
assertType<
|
assert.type<
|
||||||
(
|
(
|
||||||
...args: unknown[]
|
...args: unknown[]
|
||||||
) => BaseFluent<unknown>
|
) => BaseFluent<unknown>
|
||||||
@@ -100,7 +110,8 @@ export const AsyncMixin = Mixin<AsyncMixin>((value, $, fluent) => {
|
|||||||
return proxy;
|
return proxy;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
if (import.meta.vitest) {
|
if (import.meta.vitest) {
|
||||||
const { test, expect } = import.meta.vitest;
|
const { test, expect } = import.meta.vitest;
|
||||||
|
|||||||
+51
-13
@@ -1,10 +1,13 @@
|
|||||||
import { makeFluent } from "../base";
|
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 Props,
|
||||||
|
type Return,
|
||||||
|
} from "../base/mixin";
|
||||||
|
|
||||||
export interface Base extends Mixin.HKT {
|
interface IBase<T = unknown, t extends Props = Props> {
|
||||||
new: (t: HKT.T<this>) => Input<typeof t> extends infer T
|
|
||||||
? {
|
|
||||||
/**
|
/**
|
||||||
* Interospect value using the specified `callback` without
|
* Interospect value using the specified `callback` without
|
||||||
* modifying the value.
|
* modifying the value.
|
||||||
@@ -20,9 +23,7 @@ export interface Base extends Mixin.HKT {
|
|||||||
* expect(value).toBe(10);
|
* expect(value).toBe(10);
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
tap(
|
tap: (callback: (value: Readonly<T>) => void) => Return<T, t>;
|
||||||
callback: (value: Readonly<T>) => void,
|
|
||||||
): Return<T, typeof t>;
|
|
||||||
/**
|
/**
|
||||||
* Put value through or pipe value through the specified
|
* Put value through or pipe value through the specified
|
||||||
* `callback` using the outputted return value as a new value.
|
* `callback` using the outputted return value as a new value.
|
||||||
@@ -37,14 +38,34 @@ export interface Base extends Mixin.HKT {
|
|||||||
* expect(value).toBe("HELLO");
|
* expect(value).toBe("HELLO");
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
transform<U>(
|
transform: <U>(callback: (value: T) => U) => Return<U, t>;
|
||||||
callback: (value: T) => U,
|
/**
|
||||||
): Return<U, typeof t>;
|
* Set value to a fallback if the specified callback returns `false`
|
||||||
}
|
* @param callback Guarding callback returning `true` to keep the value
|
||||||
: never;
|
* @param fallback The fallback value, `null` by default
|
||||||
|
* @from {@link Base `Base`}
|
||||||
|
* @example
|
||||||
|
* const isObject = (value: unknown) => typeof value === 'object';
|
||||||
|
*
|
||||||
|
* const a = $({ version: '1.0.0' }).where(isObject).value;
|
||||||
|
* const b = $(42).where(isObject).value;
|
||||||
|
* const c = $(42).where(isObject, { version: '0.1.0' }).value;
|
||||||
|
*
|
||||||
|
* expect(a).toMatchObject({ version: '1.0.0' });
|
||||||
|
* expect(b).toBe(null);
|
||||||
|
* expect(c).toMatchObject({ version: '0.1.0' });
|
||||||
|
*/
|
||||||
|
where: <const U = null>(
|
||||||
|
callback: (v: T) => boolean,
|
||||||
|
fallback?: U,
|
||||||
|
) => Return<T | U, t>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Base = Mixin<Base>((value, $, fluent) => {
|
export interface Base extends Mixin.HKT<IBase> {
|
||||||
|
new: (t: HKT.T<this>) => IBase<Input<typeof t>, typeof t>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Base = Mixin<IBase, Base>((value, $, fluent) => {
|
||||||
$.tap = (callback: (value: unknown) => void) => {
|
$.tap = (callback: (value: unknown) => void) => {
|
||||||
callback(value);
|
callback(value);
|
||||||
return fluent(value);
|
return fluent(value);
|
||||||
@@ -53,6 +74,11 @@ export const Base = Mixin<Base>((value, $, fluent) => {
|
|||||||
$.transform = (callback: (value: unknown) => unknown) => {
|
$.transform = (callback: (value: unknown) => unknown) => {
|
||||||
return fluent(callback(value));
|
return fluent(callback(value));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$.where = (
|
||||||
|
callback: (value: unknown) => boolean,
|
||||||
|
fallback: unknown = null,
|
||||||
|
) => fluent(callback(value) ? value : fallback);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (import.meta.vitest) {
|
if (import.meta.vitest) {
|
||||||
@@ -83,4 +109,16 @@ if (import.meta.vitest) {
|
|||||||
$(value).transform(increment).transform(increment).value,
|
$(value).transform(increment).transform(increment).value,
|
||||||
).toBe(increment(increment(value)));
|
).toBe(increment(increment(value)));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("where()", () => {
|
||||||
|
const even = 4 as const;
|
||||||
|
const odd = 7 as const;
|
||||||
|
|
||||||
|
const isEven = (v: number) => v % 2 === 0;
|
||||||
|
|
||||||
|
expect($(even).where(isEven).value).toBe(even);
|
||||||
|
expect($(odd).where(isEven).value).toBe(null);
|
||||||
|
|
||||||
|
expect($(odd).where(isEven, -1).value).toBe(-1);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
+30
-713
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,195 @@
|
|||||||
|
import type { Inc, Vec } from "../../utility";
|
||||||
|
|
||||||
|
export type Floor<T extends number> =
|
||||||
|
`${T}` extends `${infer N extends number}.${number}` ? N : T;
|
||||||
|
export type Ceil<T extends number> =
|
||||||
|
`${T}` extends `${infer N extends number}.${number}` ? Inc<N> : T;
|
||||||
|
export type Round<T extends number> =
|
||||||
|
`${T}` extends `${infer N extends number}.${0 | 1 | 2 | 3 | 4}${number | ""}`
|
||||||
|
? N
|
||||||
|
: Ceil<T>;
|
||||||
|
|
||||||
|
export type Sign<T extends number> = T extends 0
|
||||||
|
? 0
|
||||||
|
: `${T}` extends `-${number}`
|
||||||
|
? -1
|
||||||
|
: 1;
|
||||||
|
|
||||||
|
export type Abs<T extends number> =
|
||||||
|
`${T}` extends `-${infer N extends number}` ? N : T;
|
||||||
|
|
||||||
|
export type Negate<T extends number> = T extends 0
|
||||||
|
? 0
|
||||||
|
: `${T}` extends `-${infer N extends number}`
|
||||||
|
? N
|
||||||
|
: `-${T}` extends `${infer N extends number}`
|
||||||
|
? N
|
||||||
|
: number;
|
||||||
|
|
||||||
|
type Fn<
|
||||||
|
N extends number = typeof Infinity,
|
||||||
|
Max extends boolean = false,
|
||||||
|
> = (
|
||||||
|
...args: [...Vec<never, N>, ...(Max extends true ? [] : never[])]
|
||||||
|
) => unknown;
|
||||||
|
|
||||||
|
interface IBase {
|
||||||
|
/**
|
||||||
|
* `x + y`
|
||||||
|
* @param other The second term of the addition (`y`)
|
||||||
|
*/
|
||||||
|
add: Fn<1>;
|
||||||
|
/**
|
||||||
|
* `x - y`
|
||||||
|
* @param other The second term of the subtraction (`y`)
|
||||||
|
*/
|
||||||
|
subtract: Fn<1>;
|
||||||
|
/**
|
||||||
|
* `x * y`
|
||||||
|
* @param factor Factor term of the multiplication (`y`)
|
||||||
|
*/
|
||||||
|
multiply: Fn<1>;
|
||||||
|
/**
|
||||||
|
* `x ** y`
|
||||||
|
* @param exponent Exponent term of the power (`y`)
|
||||||
|
*/
|
||||||
|
pow: Fn<1, true>;
|
||||||
|
/**
|
||||||
|
* `Math.sqrt(x)` (or `Math.log2(x)`)
|
||||||
|
*/
|
||||||
|
sqrt: Fn<0, true>;
|
||||||
|
/**
|
||||||
|
* `x / y`
|
||||||
|
* @param divisor Divisor term of the division (`y`)
|
||||||
|
*/
|
||||||
|
divide: Fn<1, true>;
|
||||||
|
/**
|
||||||
|
* `x mod y`, not to be confused with `x % y` (remainder operation)
|
||||||
|
* @param divisor Divisor term of the modulo operation (`y`)
|
||||||
|
* @see `.rem()` for the remainder operation
|
||||||
|
*/
|
||||||
|
mod: Fn<1, true>;
|
||||||
|
/**
|
||||||
|
* `x % y` remainder operation, not to be confused with `x mod y` (modulo)
|
||||||
|
* @param divisor Divisor term of the remainder operation (`y`)
|
||||||
|
* @see `.mod()` for the true modulo operation
|
||||||
|
*/
|
||||||
|
rem: Fn<1, true>;
|
||||||
|
/**
|
||||||
|
* `ln(x)`, `log(x, base)`, or alternatively `Math.log(x) / Math.log(base)`
|
||||||
|
* @param base Base of the logarithm (`base`). Defaults to `Math.E` for a natural logarithm function
|
||||||
|
*/
|
||||||
|
log: Fn<0, true>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `Math.floor(x)`
|
||||||
|
*/
|
||||||
|
floor: Fn<0, true>;
|
||||||
|
/**
|
||||||
|
* `Math.ceil(x)`
|
||||||
|
*/
|
||||||
|
ceil: Fn<0, true>;
|
||||||
|
/**
|
||||||
|
* `Math.round(x)` or `Math.round(x / multiple) * multiple` to round to the
|
||||||
|
* specified multiple
|
||||||
|
* @param multiple Multiple to round to, 1 if unspecified
|
||||||
|
*/
|
||||||
|
round: Fn<1, true>;
|
||||||
|
/**
|
||||||
|
* `Math.fround(x)`, round to the nearest 32-bit float
|
||||||
|
* approximation of value
|
||||||
|
*/
|
||||||
|
fround: Fn<0, true>;
|
||||||
|
/**
|
||||||
|
* `Math.f16round(x)`, round to the nearest 16-bit float
|
||||||
|
* approximation of value
|
||||||
|
*/
|
||||||
|
ffround: Fn<0, true>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `Math.abs(x)`, calculate the absolute value (distance
|
||||||
|
* from 0).
|
||||||
|
*/
|
||||||
|
abs: Fn<0, true>;
|
||||||
|
/**
|
||||||
|
* `Math.sign(x)`, returns the sign (1 or -1) of the value or zero
|
||||||
|
*/
|
||||||
|
sign: Fn<0, true>;
|
||||||
|
/**
|
||||||
|
* `-x` or alternatively `x * -1`
|
||||||
|
*/
|
||||||
|
negate: Fn<0, true>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `Math.min(x, ...values)`
|
||||||
|
* @param values Other canidates
|
||||||
|
*/
|
||||||
|
min: Fn<0>;
|
||||||
|
/**
|
||||||
|
* `Math.min(x, ...values)`
|
||||||
|
* @param values Other canidates
|
||||||
|
*/
|
||||||
|
max: Fn<0>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `Math.min(Math.max(x, min), max)`
|
||||||
|
* @param min Smallest allowed value to clamp to
|
||||||
|
* @param max Largest allowed value to clamp to
|
||||||
|
*/
|
||||||
|
clamp: Fn<2, true>;
|
||||||
|
/**
|
||||||
|
* Clamps value into the range [0.0, 1.0]
|
||||||
|
* @see `.clamp()` for a customizable range
|
||||||
|
*/
|
||||||
|
saturate: Fn<0, true>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the value at the specified point `t` on the line from the
|
||||||
|
* current value (`a`) corresponding to 0.0 to the target value (`b`)
|
||||||
|
* corresponding to 1.0. Linear interpolation from `a` to `b` at `t`.
|
||||||
|
*
|
||||||
|
* `t` is not constrained or clamped to the range [0.0, 1.0], values beyond
|
||||||
|
* that range extrapolate linearly beyond `a` and `b`.
|
||||||
|
*
|
||||||
|
* Equivalent to `lerp(a, b, t)`, `a * (1 - t) + b * t`, and `a + (b - a) *
|
||||||
|
* t`
|
||||||
|
* @param b Target value to interpolate to
|
||||||
|
* @param t Interpolation factor, the point on the line where 0.0 is `a` and 1.0 is `b`
|
||||||
|
* @see `.inverseLerp()` for the inverse operation (to calculate `t`)
|
||||||
|
*/
|
||||||
|
lerp: Fn<2, true>;
|
||||||
|
/**
|
||||||
|
* Normalize the value to the interpolation factor between the specified `a`
|
||||||
|
* and `b` range. This effectively computes the interpolation factor (`t`)
|
||||||
|
* of the linear interpolation `lerp(a, b, t)`.
|
||||||
|
*
|
||||||
|
* If the value is outside the bounds of the specified range, it will not be
|
||||||
|
* constrained or clamped to the range [0.0, 1.0].
|
||||||
|
*
|
||||||
|
* Equivalent to a range normalization or `(v - a) / (b - a)`
|
||||||
|
* @param a The 0.0 point of the range
|
||||||
|
* @param b The 1.0 point of the range
|
||||||
|
* @see `.lerp()` for the inverse operation
|
||||||
|
*/
|
||||||
|
inverseLerp: Fn<2, true>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Base<
|
||||||
|
I extends IBase,
|
||||||
|
OmitDocKeys extends keyof IBase = never,
|
||||||
|
> = Omit<{ [K in keyof IBase]: unknown }, OmitDocKeys> & I;
|
||||||
|
|
||||||
|
export namespace Base {
|
||||||
|
export interface Interface extends IBase {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function log(x: number, base: number) {
|
||||||
|
if (base === 2) return Math.log2(x);
|
||||||
|
if (base === 10) return Math.log10(x);
|
||||||
|
if (base === Math.E) return Math.log(x);
|
||||||
|
return Math.log(x) / Math.log(base);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function clamp(x: number, min: number, max: number) {
|
||||||
|
return Math.min(Math.max(x, min), max);
|
||||||
|
}
|
||||||
@@ -0,0 +1,277 @@
|
|||||||
|
import { makeFluent } from "../../base";
|
||||||
|
import type { HKT } from "../../base/hkt";
|
||||||
|
import { Mixin, type Props, type Return } from "../../base/mixin";
|
||||||
|
import { assert, type HidePrototype } from "../../internal";
|
||||||
|
import type { Apply, Vec } from "../../utility";
|
||||||
|
import { Math as M } from "../math";
|
||||||
|
import {
|
||||||
|
clamp,
|
||||||
|
log,
|
||||||
|
type Abs,
|
||||||
|
type Base,
|
||||||
|
type Ceil,
|
||||||
|
type Floor,
|
||||||
|
type Negate,
|
||||||
|
type Round,
|
||||||
|
type Sign,
|
||||||
|
} from "./base";
|
||||||
|
|
||||||
|
type Arg<T extends readonly number[]> = Vec<number, T["length"]>;
|
||||||
|
|
||||||
|
interface FloorHkt extends HKT<number, number> {
|
||||||
|
new: (t: HKT.T<this>) => Floor<typeof t>;
|
||||||
|
}
|
||||||
|
interface CeilHkt extends HKT<number, number> {
|
||||||
|
new: (t: HKT.T<this>) => Ceil<typeof t>;
|
||||||
|
}
|
||||||
|
interface RoundHkt extends HKT<number, number> {
|
||||||
|
new: (t: HKT.T<this>) => Round<typeof t>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AbsHkt extends HKT<number, number> {
|
||||||
|
new: (t: HKT.T<this>) => Abs<typeof t>;
|
||||||
|
}
|
||||||
|
interface SignHkt extends HKT<number, number> {
|
||||||
|
new: (t: HKT.T<this>) => Sign<typeof t>;
|
||||||
|
}
|
||||||
|
interface NegateHkt extends HKT<number, number> {
|
||||||
|
new: (t: HKT.T<this>) => Negate<typeof t>;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace IList {
|
||||||
|
export type BaseImpl<
|
||||||
|
T extends readonly number[],
|
||||||
|
t extends Props,
|
||||||
|
> = Base<{
|
||||||
|
add: (
|
||||||
|
other: number extends T["length"]
|
||||||
|
? number
|
||||||
|
: number | Arg<T>,
|
||||||
|
) => Return<Arg<T>, t>;
|
||||||
|
subtract: (
|
||||||
|
other: number extends T["length"]
|
||||||
|
? number
|
||||||
|
: number | Arg<T>,
|
||||||
|
) => Return<Arg<T>, t>;
|
||||||
|
multiply: (factor: number) => Return<Arg<T>, t>;
|
||||||
|
pow: (exponent: number) => Return<Arg<T>, t>;
|
||||||
|
sqrt: () => Return<Arg<T>, t>;
|
||||||
|
divide: (divisor: number) => Return<Arg<T>, t>;
|
||||||
|
mod: (divisor: number) => Return<Arg<T>, t>;
|
||||||
|
rem: (divisor: number) => Return<Arg<T>, t>;
|
||||||
|
log: (base?: number) => Return<Arg<T>, t>;
|
||||||
|
|
||||||
|
floor: () => Return<Apply<FloorHkt, T>, t>;
|
||||||
|
ceil: () => Return<Apply<CeilHkt, T>, t>;
|
||||||
|
round: <N extends readonly [number] | readonly []>(
|
||||||
|
...multiple: N
|
||||||
|
) => Return<
|
||||||
|
N extends readonly [number] ? Arg<T> : Apply<RoundHkt, T>,
|
||||||
|
t
|
||||||
|
>;
|
||||||
|
fround: () => Return<Arg<T>, t>;
|
||||||
|
ffround: () => Return<Arg<T>, t>;
|
||||||
|
|
||||||
|
abs: () => Return<Apply<AbsHkt, T>, t>;
|
||||||
|
sign: () => Return<Apply<SignHkt, T>, t>;
|
||||||
|
negate: () => Return<Apply<NegateHkt, T>, t>;
|
||||||
|
|
||||||
|
min: <U extends number[]>(
|
||||||
|
...others: U
|
||||||
|
) => Return<T[number] | U[number], t>;
|
||||||
|
max: <U extends number[]>(
|
||||||
|
...others: U
|
||||||
|
) => Return<T[number] | U[number], t>;
|
||||||
|
clamp: <Min extends number, Max extends number>(
|
||||||
|
min: Min,
|
||||||
|
max: Max,
|
||||||
|
) => Return<Apply<Apply.Union<Min | Max>, T>, t>;
|
||||||
|
saturate: () => Return<Arg<T>, t>;
|
||||||
|
|
||||||
|
lerp: (b: Arg<T>, t: number) => Return<Arg<T>, t>;
|
||||||
|
inverseLerp: (a: Arg<T>, b: Arg<T>) => Return<Arg<T>, t>;
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type IList<
|
||||||
|
T extends readonly number[] = readonly number[],
|
||||||
|
t extends Props = Props,
|
||||||
|
> = T extends readonly [] | readonly [number]
|
||||||
|
? {}
|
||||||
|
: IList.BaseImpl<T, t> & {
|
||||||
|
sum: () => Return<number, t>;
|
||||||
|
product: () => Return<number, t>;
|
||||||
|
|
||||||
|
findMin: () => Return<keyof T, t>;
|
||||||
|
findMax: () => Return<keyof T, t>;
|
||||||
|
|
||||||
|
average: () => Return<number, t>;
|
||||||
|
median: () => Return<number, t>;
|
||||||
|
mode: () => Return<number[], t>;
|
||||||
|
range: () => Return<number, t>;
|
||||||
|
|
||||||
|
variance: (() => Return<number, t>) & {
|
||||||
|
sample: () => Return<number, t>;
|
||||||
|
} & HidePrototype;
|
||||||
|
deviation: (() => Return<number, t>) & {
|
||||||
|
sample: () => Return<number, t>;
|
||||||
|
} & HidePrototype;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const applyList = Mixin.partial<IList, readonly number[]>(
|
||||||
|
(value, $, fluent) => {
|
||||||
|
$.add = (other: number | number[]) => {
|
||||||
|
if (typeof other === "object") {
|
||||||
|
assert(
|
||||||
|
value.length === other.length,
|
||||||
|
"List.add() parameters must be of equal length",
|
||||||
|
);
|
||||||
|
return fluent(value.map((x, idx) => x + other[idx]));
|
||||||
|
}
|
||||||
|
return fluent(value.map((x) => x + other));
|
||||||
|
};
|
||||||
|
$.subtract = (other: number | number[]) => {
|
||||||
|
if (typeof other === "object") {
|
||||||
|
assert(
|
||||||
|
value.length === other.length,
|
||||||
|
"List.subtract() parameters must be of equal length",
|
||||||
|
);
|
||||||
|
return fluent(value.map((x, idx) => x - other[idx]));
|
||||||
|
}
|
||||||
|
return fluent(value.map((x) => x - other));
|
||||||
|
};
|
||||||
|
$.multiply = (factor: number) =>
|
||||||
|
fluent(value.map((x) => x * factor));
|
||||||
|
$.pow = (exponent: number) =>
|
||||||
|
fluent(value.map((x) => x ** exponent));
|
||||||
|
$.sqrt = () => fluent(value.map(Math.sqrt));
|
||||||
|
$.divide = (divisor: number) =>
|
||||||
|
fluent(value.map((x) => x / divisor));
|
||||||
|
$.mod = (divisor: number) =>
|
||||||
|
fluent(
|
||||||
|
value.map((x) => ((x % divisor) + divisor) % divisor),
|
||||||
|
);
|
||||||
|
$.rem = (divisor: number) =>
|
||||||
|
fluent(value.map((x) => x % divisor));
|
||||||
|
$.log = (base: number = Math.E) =>
|
||||||
|
fluent(value.map((x) => log(x, base)));
|
||||||
|
|
||||||
|
$.floor = () => fluent(value.map(Math.floor));
|
||||||
|
$.ceil = () => fluent(value.map(Math.ceil));
|
||||||
|
$.round = (multiple?: number) =>
|
||||||
|
fluent(
|
||||||
|
multiple !== undefined
|
||||||
|
? value.map(
|
||||||
|
(x) =>
|
||||||
|
Math.round(x / multiple) * multiple,
|
||||||
|
)
|
||||||
|
: value.map(Math.floor),
|
||||||
|
);
|
||||||
|
$.fround = () => fluent(value.map(Math.fround));
|
||||||
|
$.ffround = () => fluent(value.map(Math.f16round));
|
||||||
|
|
||||||
|
$.abs = () => fluent(value.map(Math.abs));
|
||||||
|
$.sign = () => fluent(value.map(Math.sign));
|
||||||
|
$.negate = () => fluent(value.map((x) => -x));
|
||||||
|
|
||||||
|
$.min = (...others: number[]) =>
|
||||||
|
fluent(Math.min(...value, ...others));
|
||||||
|
$.max = (...others: number[]) =>
|
||||||
|
fluent(Math.max(...value, ...others));
|
||||||
|
$.clamp = (min: number, max: number) =>
|
||||||
|
fluent(value.map((x) => clamp(x, min, max)));
|
||||||
|
$.saturate = () => fluent(value.map((x) => clamp(x, 0, 1)));
|
||||||
|
|
||||||
|
$.lerp = (b: number[], t: number) => {
|
||||||
|
assert(
|
||||||
|
value.length === b.length,
|
||||||
|
"List.lerp() a and b parameters must be of equal length",
|
||||||
|
);
|
||||||
|
return fluent(
|
||||||
|
value.map((x, idx) => x + (b[idx] - x) * t),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
$.inverseLerp = (a: number[], b: number[]) => {
|
||||||
|
assert(
|
||||||
|
value.length === a.length && a.length === b.length,
|
||||||
|
"List.inverseLerp() a and b parameters must be of equal length",
|
||||||
|
);
|
||||||
|
return fluent(
|
||||||
|
value.map(
|
||||||
|
(x, idx) => (x - a[idx]) / (b[idx] - a[idx]),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
$.sum = () => fluent(value.reduce((a, b) => a + b, 0));
|
||||||
|
$.product = () => fluent(value.reduce((a, b) => a * b, 1));
|
||||||
|
|
||||||
|
$.findMin = () => fluent(value.indexOf(Math.min(...value)));
|
||||||
|
$.findMax = () => fluent(value.indexOf(Math.max(...value)));
|
||||||
|
|
||||||
|
$.average = () =>
|
||||||
|
fluent(value.reduce((a, b) => a + b, 0) / value.length);
|
||||||
|
$.median = () => {
|
||||||
|
const sorted = value.toSorted((a, b) => a - b);
|
||||||
|
const middle = (sorted.length - 1) / 2;
|
||||||
|
|
||||||
|
if (sorted.length % 2 === 1) return sorted[middle];
|
||||||
|
|
||||||
|
const a = sorted[Math.floor(middle)];
|
||||||
|
const b = sorted[Math.ceil(middle)];
|
||||||
|
|
||||||
|
return (a + b) / 2;
|
||||||
|
};
|
||||||
|
$.mode = () => {
|
||||||
|
const occurances = new Map<number, number>();
|
||||||
|
let max = 0;
|
||||||
|
|
||||||
|
for (const v of value) {
|
||||||
|
const count = (occurances.get(v) ?? 0) + 1;
|
||||||
|
max = Math.max(max, count);
|
||||||
|
occurances.set(v, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
const output: number[] = [];
|
||||||
|
occurances.forEach((count, value) => {
|
||||||
|
if (count === max) output.push(value);
|
||||||
|
});
|
||||||
|
|
||||||
|
return fluent(output);
|
||||||
|
};
|
||||||
|
|
||||||
|
$.range = () =>
|
||||||
|
fluent(Math.max(...value) - Math.min(...value));
|
||||||
|
|
||||||
|
const variance = (sample: boolean) => {
|
||||||
|
if (value.length === 0) return NaN;
|
||||||
|
if (value.length === 1) return sample ? NaN : 0;
|
||||||
|
|
||||||
|
const mean =
|
||||||
|
value.reduce((a, b) => a + b, 0) / value.length;
|
||||||
|
const deviations = value.map((x) => (x - mean) ** 2);
|
||||||
|
|
||||||
|
const sum = deviations.reduce((a, b) => a + b, 0);
|
||||||
|
|
||||||
|
if (sample) return sum / (deviations.length - 1);
|
||||||
|
return sum / deviations.length;
|
||||||
|
};
|
||||||
|
$.variance = Object.assign(() => fluent(variance(false)), {
|
||||||
|
sample: () => fluent(variance(true)),
|
||||||
|
});
|
||||||
|
$.deviation = Object.assign(
|
||||||
|
() => fluent(Math.sqrt(variance(false))),
|
||||||
|
{
|
||||||
|
sample: () => fluent(Math.sqrt(variance(true))),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (import.meta.vitest) {
|
||||||
|
const { test, expect, expectTypeOf, describe } = import.meta
|
||||||
|
.vitest;
|
||||||
|
|
||||||
|
const registry = [M] as const;
|
||||||
|
const $ = makeFluent(registry);
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
+44
-14
@@ -1,5 +1,5 @@
|
|||||||
import { makeFluent } from "../../base";
|
import { makeFluent } from "../../base";
|
||||||
import type { Props, Return } from "../../base/mixin";
|
import { Mixin, type Props, type Return } from "../../base/mixin";
|
||||||
import { Math } from "../math";
|
import { Math } from "../math";
|
||||||
import type { Vec, Dec, Inc as __Inc } from "../../utility";
|
import type { Vec, Dec, Inc as __Inc } from "../../utility";
|
||||||
|
|
||||||
@@ -98,23 +98,37 @@ type Primative<
|
|||||||
: never
|
: never
|
||||||
: TOut;
|
: TOut;
|
||||||
|
|
||||||
|
interface Swizzle4D extends SwizzlePermutations<4> {}
|
||||||
|
|
||||||
type SwizzleCache = [
|
type SwizzleCache = [
|
||||||
{ x: [0] },
|
{ x: [0] },
|
||||||
SwizzlePermutations<2>,
|
SwizzlePermutations<2>,
|
||||||
SwizzlePermutations<3>,
|
SwizzlePermutations<3>,
|
||||||
SwizzlePermutations<4>,
|
Swizzle4D,
|
||||||
];
|
];
|
||||||
|
|
||||||
export type Swizzle<
|
export type Swizzle<
|
||||||
T extends readonly unknown[],
|
T extends readonly unknown[],
|
||||||
t extends Props,
|
t extends Props,
|
||||||
> = number extends T["length"]
|
> = number extends T["length"]
|
||||||
|
? unknown
|
||||||
|
: T extends readonly []
|
||||||
? unknown
|
? unknown
|
||||||
: (
|
: (
|
||||||
Dec<T["length"]> extends infer K extends 0 | 1 | 2 | 3
|
Dec<T["length"]> extends infer K extends
|
||||||
|
| 0
|
||||||
|
| 1
|
||||||
|
| 2
|
||||||
|
| 3
|
||||||
? { k: K; c: SwizzleCache[K] }
|
? { k: K; c: SwizzleCache[K] }
|
||||||
: SwizzleCache extends [...unknown[], infer Last]
|
: SwizzleCache extends [
|
||||||
? { k: Dec<SwizzleCache["length"]>; c: Last }
|
...unknown[],
|
||||||
|
infer Last,
|
||||||
|
]
|
||||||
|
? {
|
||||||
|
k: Dec<SwizzleCache["length"]>;
|
||||||
|
c: Last;
|
||||||
|
}
|
||||||
: never
|
: never
|
||||||
) extends {
|
) extends {
|
||||||
k: infer K extends number;
|
k: infer K extends number;
|
||||||
@@ -135,13 +149,22 @@ export type Swizzle<
|
|||||||
Primative<T, t>[K]
|
Primative<T, t>[K]
|
||||||
: never;
|
: never;
|
||||||
|
|
||||||
|
export type ISwizzle<
|
||||||
|
T extends readonly unknown[] = readonly unknown[],
|
||||||
|
t extends Props = Props,
|
||||||
|
> = {
|
||||||
|
readonly [K in keyof Swizzle4D]: Return<
|
||||||
|
Sequence<T, Swizzle4D[K]>,
|
||||||
|
t
|
||||||
|
>;
|
||||||
|
} & Primative<T, t>[3];
|
||||||
|
|
||||||
const axis = ["x", "y", "z", "w"] as const;
|
const axis = ["x", "y", "z", "w"] as const;
|
||||||
|
|
||||||
export function applySwizzle(
|
export const applySwizzle = Mixin.partial<
|
||||||
value: number[],
|
ISwizzle,
|
||||||
$: object,
|
readonly unknown[]
|
||||||
fluent: (value: unknown) => never,
|
>((value, $, fluent) => {
|
||||||
) {
|
|
||||||
const length = globalThis.Math.min(value.length, axis.length);
|
const length = globalThis.Math.min(value.length, axis.length);
|
||||||
const state = new Array<number>(length).fill(-1);
|
const state = new Array<number>(length).fill(-1);
|
||||||
|
|
||||||
@@ -197,10 +220,11 @@ export function applySwizzle(
|
|||||||
return fluent(value[3]);
|
return fluent(value[3]);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
|
|
||||||
if (import.meta.vitest) {
|
if (import.meta.vitest) {
|
||||||
const { test, expect, expectTypeOf } = import.meta.vitest;
|
const { test, describe, expect, expectTypeOf } = import.meta
|
||||||
|
.vitest;
|
||||||
|
|
||||||
const registry = [Math] as const;
|
const registry = [Math] as const;
|
||||||
const $ = makeFluent(registry);
|
const $ = makeFluent(registry);
|
||||||
@@ -263,6 +287,7 @@ if (import.meta.vitest) {
|
|||||||
expectTypeOf(v).toEqualTypeOf(w);
|
expectTypeOf(v).toEqualTypeOf(w);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("types", () => {
|
||||||
test("Next", () => {
|
test("Next", () => {
|
||||||
type N = 3;
|
type N = 3;
|
||||||
|
|
||||||
@@ -278,7 +303,9 @@ if (import.meta.vitest) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("Key", () => {
|
test("Key", () => {
|
||||||
expectTypeOf<Key<[1, 2, undefined]>>().toEqualTypeOf<"yz">();
|
expectTypeOf<
|
||||||
|
Key<[1, 2, undefined]>
|
||||||
|
>().toEqualTypeOf<"yz">();
|
||||||
expectTypeOf<Key<[1, 1, 1]>>().toEqualTypeOf<"yyy">();
|
expectTypeOf<Key<[1, 1, 1]>>().toEqualTypeOf<"yyy">();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -287,7 +314,9 @@ if (import.meta.vitest) {
|
|||||||
type Out = [5, 4, 3, 2, 1];
|
type Out = [5, 4, 3, 2, 1];
|
||||||
type Reverse = [4, 3, 2, 1, 0];
|
type Reverse = [4, 3, 2, 1, 0];
|
||||||
|
|
||||||
expectTypeOf<Sequence<In, Reverse>>().toEqualTypeOf<Out>();
|
expectTypeOf<
|
||||||
|
Sequence<In, Reverse>
|
||||||
|
>().toEqualTypeOf<Out>();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Primative", () => {
|
test("Primative", () => {
|
||||||
@@ -302,4 +331,5 @@ if (import.meta.vitest) {
|
|||||||
expectTypeOf<V>().toHaveProperty("y");
|
expectTypeOf<V>().toHaveProperty("y");
|
||||||
expectTypeOf<V>().not.toHaveProperty("z");
|
expectTypeOf<V>().not.toHaveProperty("z");
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
+44
-87
@@ -6,7 +6,7 @@ import {
|
|||||||
type Props,
|
type Props,
|
||||||
type Return,
|
type Return,
|
||||||
} from "../base/mixin";
|
} from "../base/mixin";
|
||||||
import { assert, assertType, type HidePrototype } from "../internal";
|
import { assert, type HidePrototype } from "../internal";
|
||||||
|
|
||||||
type NoneSentinel = null | undefined;
|
type NoneSentinel = null | undefined;
|
||||||
type None<T> = Extract<T, NoneSentinel>;
|
type None<T> = Extract<T, NoneSentinel>;
|
||||||
@@ -16,35 +16,7 @@ function isNone<T>(v: T): v is None<T> {
|
|||||||
return v === null || v === undefined;
|
return v === null || v === undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Where<T, t extends Props> {
|
interface IOptional<T = unknown, t extends Props = Props> {
|
||||||
/**
|
|
||||||
* Set a value to a fallback if it does not conform to some conditional
|
|
||||||
* @param callback Callback returning a boolean, `false` sets the value to the fallback
|
|
||||||
* @param fallback The fallback value, `null` by default
|
|
||||||
* @example
|
|
||||||
* const parse = () => { version: "1.2.4" } as unknown;
|
|
||||||
*
|
|
||||||
* const version = $(parse())
|
|
||||||
* .where(v => typeof v === 'object' &&
|
|
||||||
* v !== null &&
|
|
||||||
* 'version' in v)
|
|
||||||
* .and(v => v.version)
|
|
||||||
* .or("1.0.0")
|
|
||||||
* .value;
|
|
||||||
* expect(version).toBe("1.2.4");
|
|
||||||
* @from {@link Optional `Optional`}
|
|
||||||
*/
|
|
||||||
where: <const U = null>(
|
|
||||||
callback: (v: T) => boolean,
|
|
||||||
fallback?: U,
|
|
||||||
) => Return<T | U, t>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Optional extends Mixin.HKT {
|
|
||||||
new: (t: HKT.T<this>) => Input<typeof t> extends infer T
|
|
||||||
? None<T> extends never
|
|
||||||
? Where<T, typeof t>
|
|
||||||
: {
|
|
||||||
/**
|
/**
|
||||||
* Transform a value via callback if it is not `null` or
|
* Transform a value via callback if it is not `null` or
|
||||||
* `undefined`
|
* `undefined`
|
||||||
@@ -65,9 +37,7 @@ export interface Optional extends Mixin.HKT {
|
|||||||
* expect(b).toBe(15);
|
* expect(b).toBe(15);
|
||||||
* @from {@link Optional `Optional`}
|
* @from {@link Optional `Optional`}
|
||||||
*/
|
*/
|
||||||
and: <U>(
|
and: <U>(callback: (v: Some<T>) => U) => Return<None<T> | U, t>;
|
||||||
callback: (v: Some<T>) => U,
|
|
||||||
) => Return<None<T> | U, typeof t>;
|
|
||||||
/**
|
/**
|
||||||
* Set value to a fallback if it is `null` or `undefined`.
|
* Set value to a fallback if it is `null` or `undefined`.
|
||||||
* @param fallback The fallback value to use. To defer
|
* @param fallback The fallback value to use. To defer
|
||||||
@@ -85,9 +55,7 @@ export interface Optional extends Mixin.HKT {
|
|||||||
* expect(b).toBe(10);
|
* expect(b).toBe(10);
|
||||||
* @from {@link Optional `Optional`}
|
* @from {@link Optional `Optional`}
|
||||||
*/
|
*/
|
||||||
or: (<const U>(
|
or: (<const U>(fallback: U) => Return<Some<T> | U, t>) & {
|
||||||
fallback: U,
|
|
||||||
) => Return<Some<T> | U, typeof t>) & {
|
|
||||||
/**
|
/**
|
||||||
* Set value to the result of `callback` if it is `null`
|
* Set value to the result of `callback` if it is `null`
|
||||||
* or `undefined`. Unlike the normal `.or()`, this method
|
* or `undefined`. Unlike the normal `.or()`, this method
|
||||||
@@ -113,7 +81,7 @@ export interface Optional extends Mixin.HKT {
|
|||||||
*/
|
*/
|
||||||
else: <U>(
|
else: <U>(
|
||||||
callback: (v: None<T>) => U,
|
callback: (v: None<T>) => U,
|
||||||
) => Return<Some<T> | U, typeof t>;
|
) => Return<Some<T> | U, t>;
|
||||||
} & HidePrototype;
|
} & HidePrototype;
|
||||||
/**
|
/**
|
||||||
* Assert that value is not `null` or `undefined`
|
* Assert that value is not `null` or `undefined`
|
||||||
@@ -133,9 +101,7 @@ export interface Optional extends Mixin.HKT {
|
|||||||
* }).toThrow()
|
* }).toThrow()
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
assert: ((
|
assert: ((msg?: string) => Return<Some<T>, t>) & {
|
||||||
msg?: string,
|
|
||||||
) => Return<Some<T>, typeof t>) & {
|
|
||||||
/**
|
/**
|
||||||
* Assert that the value is either `null` or `undefined`
|
* Assert that the value is either `null` or `undefined`
|
||||||
* @param msg Reasoning to attach to the `AssertionError`
|
* @param msg Reasoning to attach to the `AssertionError`
|
||||||
@@ -153,74 +119,77 @@ export interface Optional extends Mixin.HKT {
|
|||||||
* }).toThrow()
|
* }).toThrow()
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
none: (
|
none: (msg?: string) => Return<None<T>, t>;
|
||||||
msg?: string,
|
|
||||||
) => Return<None<T>, typeof t>;
|
|
||||||
} & HidePrototype;
|
} & HidePrototype;
|
||||||
} & Where<T, typeof t>
|
}
|
||||||
|
|
||||||
|
export interface Optional extends Mixin.HKT<IOptional> {
|
||||||
|
new: (
|
||||||
|
t: HKT.T<this>,
|
||||||
|
) => Input<typeof t> extends infer T
|
||||||
|
? None<T> extends never
|
||||||
|
? {}
|
||||||
|
: IOptional<T, typeof t>
|
||||||
: never;
|
: never;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Optional = Mixin<Optional>((value, $, fluent) => {
|
export const Optional = Mixin<IOptional, Optional>(
|
||||||
|
(value, $, fluent) => {
|
||||||
if (isNone(value)) {
|
if (isNone(value)) {
|
||||||
$.and = () => {
|
$.and = () => {
|
||||||
return fluent(value);
|
return fluent(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
$.or = (fallback: unknown) => {
|
$.or = Object.assign(
|
||||||
|
(fallback: unknown) => {
|
||||||
return fluent(fallback);
|
return fluent(fallback);
|
||||||
};
|
},
|
||||||
assertType<object>($.or);
|
{
|
||||||
Object.assign($.or, {
|
|
||||||
else: (callback: (v: unknown) => unknown) => {
|
else: (callback: (v: unknown) => unknown) => {
|
||||||
return fluent(callback(value));
|
return fluent(callback(value));
|
||||||
},
|
},
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
$.assert = (msg?: string) => {
|
$.assert = Object.assign(
|
||||||
|
(msg?: string) => {
|
||||||
assert(false, msg);
|
assert(false, msg);
|
||||||
};
|
},
|
||||||
assertType<object>($.assert);
|
{
|
||||||
Object.assign($.assert, {
|
|
||||||
none: () => {
|
none: () => {
|
||||||
return fluent(value);
|
return fluent(value);
|
||||||
},
|
},
|
||||||
});
|
},
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
$.and = (callback: (v: unknown) => unknown) => {
|
$.and = (callback: (v: unknown) => unknown) => {
|
||||||
return fluent(callback(value));
|
return fluent(callback(value));
|
||||||
};
|
};
|
||||||
|
|
||||||
$.or = () => {
|
$.or = Object.assign(
|
||||||
|
() => {
|
||||||
return fluent(value);
|
return fluent(value);
|
||||||
};
|
},
|
||||||
|
{
|
||||||
assertType<object>($.or);
|
|
||||||
Object.assign($.or, {
|
|
||||||
else: () => {
|
else: () => {
|
||||||
return fluent(value);
|
return fluent(value);
|
||||||
},
|
},
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
$.assert = () => {
|
$.assert = Object.assign(
|
||||||
|
() => {
|
||||||
return fluent(value);
|
return fluent(value);
|
||||||
};
|
},
|
||||||
assertType<object>($.assert);
|
{
|
||||||
Object.assign($.assert, {
|
|
||||||
none: (msg?: string) => {
|
none: (msg?: string) => {
|
||||||
assert(false, msg);
|
assert(false, msg);
|
||||||
},
|
},
|
||||||
});
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
$.where = (
|
);
|
||||||
callback: (v: unknown) => boolean,
|
|
||||||
fallback = null,
|
|
||||||
) => {
|
|
||||||
if (callback(value)) return fluent(value);
|
|
||||||
return fluent(fallback);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
if (import.meta.vitest) {
|
if (import.meta.vitest) {
|
||||||
const { test, expect, expectTypeOf, vi } = import.meta.vitest;
|
const { test, expect, expectTypeOf, vi } = import.meta.vitest;
|
||||||
@@ -272,16 +241,4 @@ if (import.meta.vitest) {
|
|||||||
|
|
||||||
expect(callback).toHaveBeenCalledOnce();
|
expect(callback).toHaveBeenCalledOnce();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("where()", () => {
|
|
||||||
const even = 4 as const;
|
|
||||||
const odd = 7 as const;
|
|
||||||
|
|
||||||
const isEven = (v: number) => v % 2 === 0;
|
|
||||||
|
|
||||||
expect($(even).where(isEven).value).toBe(even);
|
|
||||||
expect($(odd).where(isEven).value).toBe(null);
|
|
||||||
|
|
||||||
expect($(odd).where(isEven, -1).value).toBe(-1);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,13 @@
|
|||||||
|
import type { HKT } from "./base/hkt";
|
||||||
|
|
||||||
export type MaxDepth = 50;
|
export type MaxDepth = 50;
|
||||||
|
|
||||||
export type Pretty<T> = { [K in keyof T]: T[K] };
|
export type Pretty<T> = { [K in keyof T]: T[K] };
|
||||||
export type Constrain<T, U> = T extends U ? T : never;
|
export type Constrain<T, U> = T extends U ? T : never;
|
||||||
|
|
||||||
|
export type IsReadonly<T extends readonly unknown[]> =
|
||||||
|
T extends unknown[] ? true : false;
|
||||||
|
|
||||||
namespace Vec {
|
namespace Vec {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
type Lookup<T, N extends number> = N extends 0
|
type Lookup<T, N extends number> = N extends 0
|
||||||
@@ -181,6 +186,35 @@ namespace Flat {
|
|||||||
}
|
}
|
||||||
export type Flat<T extends readonly unknown[]> = Flat.Flat<T>;
|
export type Flat<T extends readonly unknown[]> = Flat.Flat<T>;
|
||||||
|
|
||||||
|
export type RecPartial<T> = {
|
||||||
|
[K in keyof T]?: RecPartial<T[K]>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export namespace Apply {
|
||||||
|
export interface Union<U> extends HKT {
|
||||||
|
new: (t: HKT.T<this>) => typeof t | U;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Intersection<U> extends HKT {
|
||||||
|
new: (t: HKT.T<this>) => typeof t & U;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Apply<
|
||||||
|
Hkt extends HKT,
|
||||||
|
T extends readonly Hkt["_in"][],
|
||||||
|
TOut extends Hkt["_out"][] = [],
|
||||||
|
> = number extends T["length"]
|
||||||
|
? Hkt["_out"][]
|
||||||
|
: TOut["length"] extends MaxDepth
|
||||||
|
? (TOut[number] | Hkt["_out"])[]
|
||||||
|
: T extends readonly [
|
||||||
|
infer Curr extends Hkt["_in"],
|
||||||
|
...infer Rest extends Hkt["_in"][],
|
||||||
|
]
|
||||||
|
? Apply<Hkt, Rest, [...TOut, HKT.Apply<Hkt, Curr>]>
|
||||||
|
: TOut;
|
||||||
|
|
||||||
if (import.meta.vitest) {
|
if (import.meta.vitest) {
|
||||||
const { test, expectTypeOf } = import.meta.vitest;
|
const { test, expectTypeOf } = import.meta.vitest;
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
/* Linting */
|
/* Linting */
|
||||||
"noUnusedLocals": false,
|
"noUnusedLocals": false,
|
||||||
"noUncheckedIndexedAccess": false,
|
"noUncheckedIndexedAccess": false,
|
||||||
"erasableSyntaxOnly": true,
|
|
||||||
},
|
},
|
||||||
"include": ["src", "tests"]
|
"include": ["src", "tests"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
"allowImportingTsExtensions": true,
|
"allowImportingTsExtensions": true,
|
||||||
"verbatimModuleSyntax": true,
|
"verbatimModuleSyntax": true,
|
||||||
"moduleDetection": "force",
|
"moduleDetection": "force",
|
||||||
|
"checkJs": true,
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
|
|
||||||
/* Linting */
|
/* Linting */
|
||||||
|
|||||||
Reference in New Issue
Block a user