From 8f1865e1f97664d55737344b7519889eab49e9a3 Mon Sep 17 00:00:00 2001 From: Phryxia Date: Tue, 23 Jan 2024 23:18:45 +0900 Subject: [PATCH] fix(reduce): better type and docs for `reduce` (#240) (#243) * fix(reduce): better type and docs for `reduce` (#240) - Change type `A`,`B` into `T`, `Acc` for `reduce` - Give proper value name for `acc` instead of `b` - Add example with explicit seed - Add @typeParam for tsdoc support * Update src/reduce.ts --------- Co-authored-by: hw --- src/reduce.ts | 151 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 99 insertions(+), 52 deletions(-) diff --git a/src/reduce.ts b/src/reduce.ts index 52749f5d..229109b8 100644 --- a/src/reduce.ts +++ b/src/reduce.ts @@ -4,21 +4,25 @@ import type Arrow from "./types/Arrow"; import type IterableInfer from "./types/IterableInfer"; import type ReturnValueType from "./types/ReturnValueType"; -function sync(f: (a: B, b: A) => B, acc: B, iterable: Iterable): B { +function sync( + f: (a: Acc, b: T) => Acc, + acc: Acc, + iterable: Iterable, +): Acc { for (const a of iterable) { acc = f(acc, a); } return acc; } -async function async( - f: (a: B, b: A) => B, - acc: Promise, - iterable: AsyncIterable, +async function async( + f: (a: Acc, b: T) => Acc, + acc: Promise, + iterable: AsyncIterable, ) { for await (const a of iterable) { // becauseof using es5, use `await` - acc = await pipe1(acc, (acc) => f(acc as B, a)); + acc = await pipe1(acc, (acc) => f(acc as Acc, a)); } return acc; } @@ -27,19 +31,48 @@ async function async( * Also known as foldl, this method boils down a list of values into a single value. * * @example + * You can reduce values into homogeneous type. + * * ```ts * const sum = (a:number, b:number) => a + b; + * + * // with implicit seed with first element * reduce(sum, [1, 2, 3, 4]); // 10 + * + * // with explicit seed * reduce(sum, 0, [1, 2, 3, 4]); // 10 + * ``` + * + * You can reduce values into heterogeneous type. + * + * ```ts + * // reduce { id: number; score: number; } to number + * reduce((acc, value) => acc + value.score, 0, [ + * { id: 0, score: 1 }, + * { id: 5, score: 2 }, + * { id: 9, score: 3 }, + * { id: 3, score: 4 } + * ]) + * ``` + * + * Omitting iterable will returns function, useful when using with pipe. * - * // with pipe + * ```ts * pipe( * [1, 2, 3, 4], * map(a => a + 10), * filter(a => a % 2 === 0), * reduce(sum), * ); // 26 + * ``` + * + * Currently, type with explicit seed form can't be inferred properly due to the limitation of typescript. + * But you can still use mostly with @ts-ignore flag. For more information please visit issue. * + * {@link https://github.com/marpple/FxTS/issues/239 | #related issue} + * + * + * ```ts * await pipe( * Promise.resolve([1, 2, 3, 4]), * map((a) => a + 10), @@ -70,52 +103,66 @@ async function async( * * see {@link https://fxts.dev/docs/pipe | pipe}, {@link https://fxts.dev/docs/toAsync | toAsync}, * {@link https://fxts.dev/docs/map | map}, {@link https://fxts.dev/docs/filter | filter} + * + * @typeParam T - Type of values in `iterable` which would be consummed. + * @typeParam Acc - Type of `acc` which is the type of accumulative value */ -function reduce(f: Arrow, seed: B, iterable: A): B; - -function reduce(f: (a: A, b: A) => A, iterable: Iterable): A; - -function reduce(f: (a: B, b: A) => B, iterable: Iterable): B; - -function reduce(f: (a: B, b: A) => B, seed: B, iterable: Iterable): B; - -function reduce( - f: (a: A, b: A) => A, - iterable: AsyncIterable, -): Promise; - -function reduce( - f: (a: B, b: A) => B | Promise, - seed: B | Promise, - iterable: AsyncIterable, -): Promise; - -function reduce( - f: (a: B, b: A) => B | Promise, - iterable: AsyncIterable, -): Promise; - -function reduce | AsyncIterable>( +function reduce( + f: Arrow, + seed: Acc, + iterable: T, +): Acc; + +function reduce(f: (acc: T, value: T) => T, iterable: Iterable): T; + +function reduce( + f: (acc: Acc, value: T) => Acc, + iterable: Iterable, +): Acc; + +function reduce( + f: (acc: Acc, value: T) => Acc, + seed: Acc, + iterable: Iterable, +): Acc; + +function reduce( + f: (acc: T, value: T) => T, + iterable: AsyncIterable, +): Promise; + +function reduce( + f: (acc: Acc, value: T) => Acc | Promise, + seed: Acc | Promise, + iterable: AsyncIterable, +): Promise; + +function reduce( + f: (acc: Acc, value: T) => Acc | Promise, + iterable: AsyncIterable, +): Promise; + +function reduce | AsyncIterable>( f: ( - a: IterableInfer, - b: IterableInfer, - ) => IterableInfer | Promise>, -): (iterable: A) => ReturnValueType>; - -function reduce | AsyncIterable, B>( - f: (a: B, b: IterableInfer) => B | Promise, -): (iterable: A) => ReturnValueType; - -function reduce | AsyncIterable, B>( - f: (a: B, b: IterableInfer) => B, - seed?: B | Iterable> | AsyncIterable>, - iterable?: Iterable> | AsyncIterable>, -): B | Promise | ((iterable: A) => ReturnValueType) { + acc: IterableInfer, + value: IterableInfer, + ) => IterableInfer | Promise>, +): (iterable: T) => ReturnValueType>; + +function reduce | AsyncIterable, Acc>( + f: (acc: Acc, value: IterableInfer) => Acc | Promise, +): (iterable: T) => ReturnValueType; + +function reduce | AsyncIterable, Acc>( + f: (acc: Acc, value: IterableInfer) => Acc, + seed?: Acc | Iterable> | AsyncIterable>, + iterable?: Iterable> | AsyncIterable>, +): Acc | Promise | ((iterable: T) => ReturnValueType) { if (iterable === undefined) { if (seed === undefined) { - return (iterable: A) => - reduce(f, iterable as any) as ReturnValueType; + return (iterable: T) => + reduce(f, iterable as any) as ReturnValueType; } if (isIterable(seed)) { @@ -124,7 +171,7 @@ function reduce | AsyncIterable, B>( if (done) { throw new TypeError("'reduce' of empty iterable with no initial value"); } - return sync(f, value as B, { + return sync(f, value as Acc, { [Symbol.iterator]() { return iterator; }, @@ -140,7 +187,7 @@ function reduce | AsyncIterable, B>( ); } - return async(f, value as Promise, { + return async(f, value as Promise, { [Symbol.asyncIterator]() { return iterator; }, @@ -152,11 +199,11 @@ function reduce | AsyncIterable, B>( } if (isIterable(iterable)) { - return sync(f, seed as B, iterable); + return sync(f, seed as Acc, iterable); } if (isAsyncIterable(iterable)) { - return async(f, Promise.resolve(seed as B), iterable); + return async(f, Promise.resolve(seed as Acc), iterable); } throw new TypeError("'iterable' must be type of Iterable or AsyncIterable");