diff --git a/.changeset/lazy-pens-cover.md b/.changeset/lazy-pens-cover.md new file mode 100644 index 00000000000..f994132c73f --- /dev/null +++ b/.changeset/lazy-pens-cover.md @@ -0,0 +1,5 @@ +--- +"effect": minor +--- + +Add NonEmpty overloads to Iterable diff --git a/packages/effect/dtslint/Iterable.tst.ts b/packages/effect/dtslint/Iterable.tst.ts new file mode 100644 index 00000000000..64c5f781b8c --- /dev/null +++ b/packages/effect/dtslint/Iterable.tst.ts @@ -0,0 +1,464 @@ +import { Iterable, Effect, Option, Predicate } from "effect" +import { NonEmptyArray } from "effect/Array" +import { hole, pipe } from "effect/Function" +import { describe, expect, it, when } from "tstyche" +import { NonEmptyIterable } from "effect/NonEmptyIterable" + +declare const nonEmptyStrings: NonEmptyIterable +declare const nonEmptyNumbers: NonEmptyIterable +declare const numbers: Iterable +declare const strings: Iterable +declare const numbersOrStrings: Iterable + +declare const predicateNumbersOrStrings: Predicate.Predicate + +const symA = Symbol.for("a") +const symB = Symbol.for("b") +const symC = Symbol.for("c") + +describe("Iterable", () => { + + it("isEmptyIterable", () => { + if (Iterable.isEmpty(numbers)) { + expect(numbers).type.toBe>() + } + // should play well with `Option.liftPredicate` + expect(Option.liftPredicate(Iterable.isEmpty)).type.toBe< + (a: Iterable) => Option.Option> + >() + }) + + it("map", () => { + expect(Iterable.map(strings, (s, i) => { + expect(s).type.toBe() + expect(i).type.toBe() + return s + "a" + })).type.toBe>() + expect(pipe( + strings, + Iterable.map((s, i) => { + expect(s).type.toBe() + expect(i).type.toBe() + return s + "a" + }) + )).type.toBe>() + + expect(Iterable.map(nonEmptyStrings, (s, i) => { + expect(s).type.toBe() + expect(i).type.toBe() + return s + "a" + })).type.toBe>() + expect(pipe( + nonEmptyStrings, + Iterable.map((s, i) => { + expect(s).type.toBe() + expect(i).type.toBe() + return s + "a" + }) + )).type.toBe>() + }) + + it("groupBy", () => { + expect(Iterable.groupBy([1, 2, 3], (n) => { + expect(n).type.toBe() + return String(n) + })).type.toBe>>() + expect(pipe( + [1, 2, 3], + Iterable.groupBy((n) => { + expect(n).type.toBe() + return String(n) + }) + )).type.toBe>>() + expect( + Iterable.groupBy([1, 2, 3], (n) => n > 0 ? "positive" as const : "negative" as const) + ).type.toBe>>() + expect(Iterable.groupBy(["a", "b"], Symbol.for)).type.toBe>>() + expect(Iterable.groupBy(["a", "b"], (s) => s === "a" ? symA : s === "b" ? symB : symC)).type.toBe< + Record> + >() + }) + + it("some", () => { + expect(Iterable.some(numbersOrStrings, (item, i) => { + expect(item).type.toBe() + expect(i).type.toBe() + return true + })).type.toBe() + expect(pipe( + numbersOrStrings, + Iterable.some((item, i) => { + expect(item).type.toBe() + expect(i).type.toBe() + return true + }) + )).type.toBe() + if (Iterable.some(numbersOrStrings, Predicate.isString)) { + expect(numbersOrStrings).type.toBe< + Iterable + >() + } + }) + + it("append", () => { + expect(Iterable.append(numbersOrStrings, true)) + .type.toBe>() + expect(pipe(numbersOrStrings, Iterable.append(true))) + .type.toBe>() + expect(Iterable.append(true)(numbersOrStrings)) + .type.toBe>() + }) + + it("prepend", () => { + expect(Iterable.prepend(numbersOrStrings, true)) + .type.toBe>() + expect(pipe(numbersOrStrings, Iterable.prepend(true))) + .type.toBe>() + expect(Iterable.prepend(true)(numbersOrStrings)) + .type.toBe>() + }) + + it("filter", () => { + expect(Iterable.filter(numbersOrStrings, (item, i) => { + expect(item).type.toBe() + expect(i).type.toBe() + return true + })).type.toBe>() + expect(pipe( + numbersOrStrings, + Iterable.filter((item, i) => { + expect(item).type.toBe() + expect(i).type.toBe() + return true + }) + )).type.toBe>() + + expect(Iterable.filter).type.not.toBeCallableWith(numbersOrStrings, (_item: string) => true) + when(pipe).isCalledWith( + numbersOrStrings, + expect(Iterable.filter).type.not.toBeCallableWith((_item: string) => true) + ) + + expect(Iterable.filter(numbers, predicateNumbersOrStrings)).type.toBe>() + expect(pipe(numbers, Iterable.filter(predicateNumbersOrStrings))).type.toBe>() + + expect(Iterable.filter(numbersOrStrings, predicateNumbersOrStrings)).type.toBe>() + expect(pipe(numbersOrStrings, Iterable.filter(predicateNumbersOrStrings))).type.toBe>() + + expect(Iterable.filter(numbersOrStrings, Predicate.isNumber)).type.toBe>() + expect(pipe(numbersOrStrings, Iterable.filter(Predicate.isNumber))).type.toBe>() + }) + + it("takeWhile", () => { + expect(Iterable.takeWhile(numbersOrStrings, (item, i) => { + expect(item).type.toBe() + expect(i).type.toBe() + return true + })).type.toBe>() + expect(pipe( + numbersOrStrings, + Iterable.takeWhile((item, i) => { + expect(item).type.toBe() + expect(i).type.toBe() + return true + }) + )).type.toBe>() + + expect(Iterable.takeWhile(numbers, predicateNumbersOrStrings)).type.toBe>() + expect(pipe(numbers, Iterable.takeWhile(predicateNumbersOrStrings))).type.toBe>() + + expect(Iterable.takeWhile(numbersOrStrings, predicateNumbersOrStrings)).type.toBe>() + expect(pipe(numbersOrStrings, Iterable.takeWhile(predicateNumbersOrStrings))).type.toBe>() + + expect(Iterable.takeWhile(numbersOrStrings, Predicate.isNumber)).type.toBe>() + expect(pipe(numbersOrStrings, Iterable.takeWhile(Predicate.isNumber))).type.toBe>() + }) + + it("findFirst", () => { + expect(Iterable.findFirst(numbersOrStrings, (item, i) => { + expect(item).type.toBe() + expect(i).type.toBe() + return true + })).type.toBe>() + expect(pipe( + numbersOrStrings, + Iterable.findFirst((item, i) => { + expect(item).type.toBe() + expect(i).type.toBe() + return true + }) + )).type.toBe>() + + expect(Iterable.findFirst(numbersOrStrings, Predicate.isNumber)).type.toBe>() + expect(pipe(numbersOrStrings, Iterable.findFirst(Predicate.isNumber))).type.toBe>() + }) + + it("findLast", () => { + expect(Iterable.findLast(numbersOrStrings, (item, i) => { + expect(item).type.toBe() + expect(i).type.toBe() + return true + })).type.toBe>() + expect(pipe( + numbersOrStrings, + Iterable.findLast((item, i) => { + expect(item).type.toBe() + expect(i).type.toBe() + return true + }) + )).type.toBe>() + + expect(Iterable.findLast(numbersOrStrings, Predicate.isNumber)).type.toBe>() + expect(pipe(numbersOrStrings, Iterable.findLast(Predicate.isNumber))).type.toBe>() + + }) + + it("flatMap", () => { + expect( + Iterable.flatMap(strings, (item, i) => { + expect(item).type.toBe() + expect(i).type.toBe() + return Iterable.empty() + }) + ).type.toBe>() + expect( + pipe( + strings, + Iterable.flatMap((item, i) => { + expect(item).type.toBe() + expect(i).type.toBe() + return Iterable.empty() + }) + ) + ).type.toBe>() + + expect( + Iterable.flatMap(nonEmptyStrings, (item, i) => { + expect(item).type.toBe() + expect(i).type.toBe() + return Iterable.empty() + }) + ).type.toBe>() + expect( + pipe( + nonEmptyStrings, + Iterable.flatMap((item, i) => { + expect(item).type.toBe() + expect(i).type.toBe() + return Iterable.empty() + }) + ) + ).type.toBe>() + + expect( + Iterable.flatMap(nonEmptyStrings, (item, i) => { + expect(item).type.toBe() + expect(i).type.toBe() + return Iterable.of(item.length) + }) + ).type.toBe>() + expect( + pipe( + nonEmptyStrings, + Iterable.flatMap((item, i) => { + expect(item).type.toBe() + expect(i).type.toBe() + return Iterable.of(item.length) + }) + ) + ).type.toBe>() + }) + + it("flatten", () => { + expect(Iterable.flatten(hole>>())).type.toBe>() + expect(Iterable.flatten(hole>>())).type.toBe>() + expect(Iterable.flatten(hole>>())).type.toBe>() + expect(Iterable.flatten(hole>>())) + .type.toBe>() + + expect( + hole>>>().pipe(Effect.map((x) => { + expect(x).type.toBe>>() + return Iterable.flatten(x) + })) + ).type.toBe, never, never>>() + expect( + hole>>>().pipe(Effect.map((x) => { + expect(x).type.toBe>>() + return Iterable.flatten(x) + })) + ).type.toBe, never, never>>() + }) + + it("prependAll", () => { + // Iterable + Iterable + expect(Iterable.prependAll(strings, numbers)).type.toBe>() + expect(pipe(strings, Iterable.prependAll(numbers))).type.toBe>() + + // NonEmptyIterable + Iterable + expect(Iterable.prependAll(nonEmptyStrings, numbers)).type.toBe>() + expect(pipe(nonEmptyStrings, Iterable.prependAll(numbers))).type.toBe>() + + // Iterable + NonEmptyIterable + expect(Iterable.prependAll(strings, nonEmptyNumbers)).type.toBe>() + expect(pipe(strings, Iterable.prependAll(nonEmptyNumbers))).type.toBe>() + + // NonEmptyIterable + NonEmptyIterable + expect(Iterable.prependAll(nonEmptyStrings, nonEmptyNumbers)).type.toBe>() + expect(pipe(nonEmptyStrings, Iterable.prependAll(nonEmptyNumbers))).type.toBe< + NonEmptyIterable + >() + }) + + it("appendAll", () => { + // Iterable + Iterable + expect(Iterable.appendAll(strings, numbers)).type.toBe>() + expect(pipe(strings, Iterable.appendAll(numbers))).type.toBe>() + + // NonEmptyIterable + Iterable + expect(Iterable.appendAll(nonEmptyStrings, numbers)).type.toBe>() + expect(pipe(nonEmptyStrings, Iterable.appendAll(numbers))).type.toBe>() + + // Iterable + NonEmptyIterable + expect(Iterable.appendAll(strings, nonEmptyNumbers)).type.toBe>() + expect(pipe(strings, Iterable.appendAll(nonEmptyNumbers))).type.toBe>() + + // NonEmptyIterable + NonEmptyIterable + expect(Iterable.appendAll(nonEmptyStrings, nonEmptyNumbers)).type.toBe>() + expect(pipe(nonEmptyStrings, Iterable.appendAll(nonEmptyNumbers))) + .type.toBe>() + }) + + it("zip", () => { + expect(Iterable.zip(strings, numbers)).type.toBe>() + expect(pipe(strings, Iterable.zip(numbers))).type.toBe>() + expect(Iterable.zip(numbers)(strings)).type.toBe>() + + expect(Iterable.zip(nonEmptyStrings, nonEmptyNumbers)).type.toBe>() + expect(pipe(nonEmptyStrings, Iterable.zip(nonEmptyNumbers))) + .type.toBe>() + expect(Iterable.zip(nonEmptyNumbers)(nonEmptyStrings)).type.toBe>() + }) + + it("intersperse", () => { + expect(Iterable.intersperse(strings, "a")).type.toBe>() + expect(pipe(strings, Iterable.intersperse("a"))).type.toBe>() + expect(Iterable.intersperse("a")(strings)).type.toBe>() + + expect(Iterable.intersperse(strings, 1)).type.toBe>() + expect(pipe(strings, Iterable.intersperse(1))).type.toBe>() + expect(Iterable.intersperse(1)(strings)).type.toBe>() + + expect(Iterable.intersperse(nonEmptyStrings, "a")).type.toBe>() + expect(pipe(nonEmptyStrings, Iterable.intersperse("a"))).type.toBe>() + expect(Iterable.intersperse("a")(nonEmptyStrings)).type.toBe>() + + expect(Iterable.intersperse(nonEmptyStrings, 1)).type.toBe>() + expect(pipe(nonEmptyStrings, Iterable.intersperse(1))).type.toBe>() + expect(Iterable.intersperse(1)(nonEmptyStrings)).type.toBe>() + }) + + it("dedupe", () => { + // Iterable + expect(Iterable.dedupe(strings)).type.toBe>() + expect(pipe(strings, Iterable.dedupe)).type.toBe>() + + // NonEmptyIterable + expect(Iterable.dedupe(nonEmptyStrings)).type.toBe>() + expect(pipe(nonEmptyStrings, Iterable.dedupe)).type.toBe>() + }) + + it("dedupeAdjacentWith", () => { + // Iterable + expect( + Iterable.dedupeAdjacentWith(strings, (a, b) => { + expect(a).type.toBe() + expect(b).type.toBe() + return true + }) + ).type.toBe>() + expect(pipe( + strings, + Iterable.dedupeAdjacentWith((a, b) => { + expect(a).type.toBe() + expect(b).type.toBe() + return true + }) + )).type.toBe>() + + // NonEmptyIterable + expect( + Iterable.dedupeAdjacentWith(nonEmptyStrings, (a, b) => { + expect(a).type.toBe() + expect(b).type.toBe() + return true + }) + ).type.toBe>() + expect( + pipe( + nonEmptyStrings, + Iterable.dedupeAdjacentWith((a, b) => { + expect(a).type.toBe() + expect(b).type.toBe() + return true + }) + ) + ).type.toBe>() + }) + + it("chunksOf", () => { + // Iterable + expect(Iterable.chunksOf(strings, 10)).type.toBe>>() + expect(pipe(strings, Iterable.chunksOf(10))).type.toBe>>() + expect(Iterable.chunksOf(10)(strings)).type.toBe>>() + + // NonEmptyIterable + expect(Iterable.chunksOf(nonEmptyStrings, 10)) + .type.toBe>>() + expect(pipe(nonEmptyStrings, Iterable.chunksOf(10))) + .type.toBe>>() + expect(Iterable.chunksOf(10)(nonEmptyStrings)) + .type.toBe>>() + }) + + it("zipWith", () => { + // Iterable + Iterable + expect( + Iterable.zipWith(strings, numbers, (a, b) => { + expect(a).type.toBe() + expect(b).type.toBe() + return [a, b] as [string, number] + }) + ).type.toBe>() + expect( + pipe( + strings, + Iterable.zipWith(numbers, (a, b) => { + expect(a).type.toBe() + expect(b).type.toBe() + return [a, b] as [string, number] + }) + ) + ).type.toBe>() + + // NonEmptyIterable + NonEmptyIterable + expect( + Iterable.zipWith(nonEmptyStrings, nonEmptyNumbers, (a, b) => { + expect(a).type.toBe() + expect(b).type.toBe() + return [a, b] as [string, number] + }) + ).type.toBe>() + expect( + pipe( + nonEmptyStrings, + Iterable.zipWith(nonEmptyNumbers, (a, b) => { + expect(a).type.toBe() + expect(b).type.toBe() + return [a, b] as [string, number] + }) + ) + ).type.toBe>() + }) + +}) diff --git a/packages/effect/src/Iterable.ts b/packages/effect/src/Iterable.ts index ef6e742818c..bfd60c26e4f 100644 --- a/packages/effect/src/Iterable.ts +++ b/packages/effect/src/Iterable.ts @@ -9,6 +9,7 @@ import type { Either } from "./Either.js" import * as E from "./Either.js" import * as Equal from "./Equal.js" import { dual, identity } from "./Function.js" +import type { NonEmptyIterable } from "./NonEmptyIterable.js" import type { Option } from "./Option.js" import * as O from "./Option.js" import { isBoolean } from "./Predicate.js" @@ -16,6 +17,37 @@ import type * as Record from "./Record.js" import * as Tuple from "./Tuple.js" import type { NoInfer } from "./Types.js" + +export declare namespace Iterable { + + type Infer> = S extends Iterable ? A + : never + + type With, A> = S extends NonEmptyIterable ? NonEmptyIterable + : Iterable + + type OrNonEmpty< + S extends Iterable, + T extends Iterable, + A + > = S extends NonEmptyIterable ? NonEmptyIterable + : T extends NonEmptyIterable ? NonEmptyIterable + : Iterable + + type AndNonEmpty< + S extends Iterable, + T extends Iterable, + A + > = S extends NonEmptyIterable ? T extends NonEmptyIterable ? NonEmptyIterable + : Iterable + : Iterable + + export type Flatten>> = T extends + NonEmptyIterable> ? NonEmptyIterable + : T extends Iterable> ? Iterable + : never +} + /** * Return a `Iterable` with element `i` initialized with `f(i)`. * @@ -36,7 +68,7 @@ import type { NoInfer } from "./Types.js" */ export const makeBy = (f: (i: number) => A, options?: { readonly length?: number -}): Iterable => { +}): NonEmptyIterable => { const max = options?.length !== undefined ? Math.max(1, Math.floor(options.length)) : Infinity return { [Symbol.iterator]() { @@ -50,7 +82,7 @@ export const makeBy = (f: (i: number) => A, options?: { } } } - } + } as NonEmptyIterable } /** @@ -69,7 +101,7 @@ export const makeBy = (f: (i: number) => A, options?: { * @category constructors * @since 2.0.0 */ -export const range = (start: number, end?: number): Iterable => { +export const range = (start: number, end?: number): NonEmptyIterable => { if (end === undefined) { return makeBy((i) => start + i) } @@ -95,8 +127,8 @@ export const range = (start: number, end?: number): Iterable => { * @since 2.0.0 */ export const replicate: { - (n: number): (a: A) => Iterable - (a: A, n: number): Iterable + (n: number): (a: A) => NonEmptyIterable + (a: A, n: number): NonEmptyIterable } = dual(2, (a: A, n: number): Iterable => makeBy(() => a, { length: n })) /** @@ -131,8 +163,8 @@ export const fromRecord = (self: Readonly>): I * @since 2.0.0 */ export const prepend: { - (head: B): (self: Iterable) => Iterable - (self: Iterable, head: B): Iterable + (head: B): (self: Iterable) => NonEmptyIterable + (self: Iterable, head: B): NonEmptyIterable } = dual(2, (self: Iterable, head: B): Iterable => prependAll(self, [head])) /** @@ -153,7 +185,11 @@ export const prepend: { * @since 2.0.0 */ export const prependAll: { - (that: Iterable): (self: Iterable) => Iterable + , T extends Iterable>( + that: T + ): (self: S) => Iterable.OrNonEmpty | Iterable.Infer> + (self: NonEmptyIterable, that: Iterable): NonEmptyIterable + (self: Iterable, that: NonEmptyIterable): NonEmptyIterable (self: Iterable, that: Iterable): Iterable } = dual( 2, @@ -167,8 +203,8 @@ export const prependAll: { * @since 2.0.0 */ export const append: { - (last: B): (self: Iterable) => Iterable - (self: Iterable, last: B): Iterable + (last: B): (self: Iterable) => NonEmptyIterable + (self: Iterable, last: B): NonEmptyIterable } = dual(2, (self: Iterable, last: B): Iterable => appendAll(self, [last])) /** @@ -178,7 +214,11 @@ export const append: { * @since 2.0.0 */ export const appendAll: { - (that: Iterable): (self: Iterable) => Iterable + , T extends Iterable>( + that: T + ): (self: S) => Iterable.OrNonEmpty | Iterable.Infer> + (self: NonEmptyIterable, that: Iterable): NonEmptyIterable + (self: Iterable, that: NonEmptyIterable): NonEmptyIterable (self: Iterable, that: Iterable): Iterable } = dual( 2, @@ -212,9 +252,9 @@ export const appendAll: { * @since 2.0.0 */ export const scan: { - (b: B, f: (b: B, a: A) => B): (self: Iterable) => Iterable - (self: Iterable, b: B, f: (b: B, a: A) => B): Iterable -} = dual(3, (self: Iterable, b: B, f: (b: B, a: A) => B): Iterable => ({ + (b: B, f: (b: B, a: A) => B): (self: Iterable) => NonEmptyIterable + (self: Iterable, b: B, f: (b: B, a: A) => B): NonEmptyIterable +} = dual(3, (self: Iterable, b: B, f: (b: B, a: A) => B): NonEmptyIterable => ({ [Symbol.iterator]() { let acc = b let iterator: Iterator | undefined @@ -232,7 +272,7 @@ export const scan: { } return { next } } -})) +}) as NonEmptyIterable) /** * Determine if an `Iterable` is empty @@ -455,7 +495,9 @@ export const findLast: { * @since 2.0.0 */ export const zip: { + (that: NonEmptyIterable): (self: NonEmptyIterable) => NonEmptyIterable<[A, B]> (that: Iterable): (self: Iterable) => Iterable<[A, B]> + (self: NonEmptyIterable, that: NonEmptyIterable): NonEmptyIterable<[A, B]> (self: Iterable, that: Iterable): Iterable<[A, B]> } = dual( 2, @@ -470,7 +512,9 @@ export const zip: { * @since 2.0.0 */ export const zipWith: { + (that: NonEmptyIterable, f: (a: A, b: B) => C): (self: NonEmptyIterable) => NonEmptyIterable (that: Iterable, f: (a: A, b: B) => C): (self: Iterable) => Iterable + (self: NonEmptyIterable, that: NonEmptyIterable, f: (a: A, b: B) => C): NonEmptyIterable (self: Iterable, that: Iterable, f: (a: A, b: B) => C): Iterable } = dual(3, (self: Iterable, that: Iterable, f: (a: A, b: B) => C): Iterable => ({ [Symbol.iterator]() { @@ -496,7 +540,10 @@ export const zipWith: { * @since 2.0.0 */ export const intersperse: { - (middle: B): (self: Iterable) => Iterable + ( + middle: B + ): >(self: S) => Iterable.With | B> + (self: NonEmptyIterable, middle: B): NonEmptyIterable (self: Iterable, middle: B): Iterable } = dual(2, (self: Iterable, middle: B): Iterable => ({ [Symbol.iterator]() { @@ -560,9 +607,10 @@ export const contains: { * @since 2.0.0 */ export const chunksOf: { - (n: number): (self: Iterable) => Iterable> - (self: Iterable, n: number): Iterable> -} = dual(2, (self: Iterable, n: number): Iterable> => { + (n: number): >(self: S) => Iterable.With>> + (self: NonEmptyIterable, n: number): NonEmptyIterable> + (self: Iterable, n: number): Iterable> +} = dual(2, (self: Iterable, n: number): Iterable> => { const safeN = Math.max(1, Math.floor(n)) return ({ [Symbol.iterator]() { @@ -578,12 +626,12 @@ export const chunksOf: { const result = iterator.next() if (result.done) { iterator = undefined - return chunk.length === 0 ? { done: true, value: undefined } : { done: false, value: chunk } + return chunk.length === 0 ? { done: true, value: undefined } : { done: false, value: chunk as NonEmptyArray } } chunk.push(result.value) } - return { done: false, value: chunk } + return { done: false, value: chunk as NonEmptyArray } } } } @@ -597,7 +645,10 @@ export const chunksOf: { * @since 2.0.0 */ export const groupWith: { - (isEquivalent: (self: A, that: A) => boolean): (self: Iterable) => Iterable> + >( + isEquivalent: (self: Iterable.Infer, that: Iterable.Infer) => boolean + ): (self: S) => Iterable.With>> + (self: NonEmptyIterable, isEquivalent: (self: A, that: A) => boolean): NonEmptyIterable> (self: Iterable, isEquivalent: (self: A, that: A) => boolean): Iterable> } = dual( 2, @@ -642,9 +693,12 @@ export const groupWith: { * @category grouping * @since 2.0.0 */ -export const group: (self: Iterable) => Iterable> = groupWith( +export const group: { + (self: NonEmptyIterable): NonEmptyIterable> + (self: Iterable): Iterable> +} = groupWith( Equal.equivalence() -) +) as any /** * Splits an `Iterable` into sub-non-empty-arrays stored in an object, based on the result of calling a `string`-returning @@ -700,16 +754,17 @@ export const empty = (): Iterable => constEmpty * @category constructors * @since 2.0.0 */ -export const of = (a: A): Iterable => [a] +export const of = (a: A): NonEmptyIterable => [a] as any /** * @category mapping * @since 2.0.0 */ export const map: { - ( - f: (a: NoInfer, i: number) => B - ): (self: Iterable) => Iterable + , B>( + f: (a: Iterable.Infer, i: number) => B + ): (self: S) => Iterable.With + (self: NonEmptyIterable, f: (a: NoInfer, i: number) => B): NonEmptyIterable (self: Iterable, f: (a: NoInfer, i: number) => B): Iterable } = dual(2, (self: Iterable, f: (a: A, i: number) => B): Iterable => ({ [Symbol.iterator]() { @@ -734,9 +789,10 @@ export const map: { * @since 2.0.0 */ export const flatMap: { - ( - f: (a: NoInfer, i: number) => Iterable - ): (self: Iterable) => Iterable + , T extends Iterable>( + f: (a: Iterable.Infer, i: number) => T + ): (self: S) => Iterable.AndNonEmpty> + (self: NonEmptyIterable, f: (a: NoInfer, i: number) => NonEmptyIterable): NonEmptyIterable (self: Iterable, f: (a: NoInfer, i: number) => Iterable): Iterable } = dual( 2, @@ -749,10 +805,10 @@ export const flatMap: { * @category sequencing * @since 2.0.0 */ -export const flatten = (self: Iterable>): Iterable => ({ +export const flatten = >>(self: S): Iterable.Flatten => ({ [Symbol.iterator]() { const outerIterator = self[Symbol.iterator]() - let innerIterator: Iterator | undefined + let innerIterator: Iterator>> | undefined function next() { if (innerIterator === undefined) { const next = outerIterator.next() @@ -770,7 +826,7 @@ export const flatten = (self: Iterable>): Iterable => ({ } return { next } } -}) +}) as any /** * @category filtering @@ -1018,7 +1074,10 @@ export const reduce: { * @since 2.0.0 */ export const dedupeAdjacentWith: { - (isEquivalent: (self: A, that: A) => boolean): (self: Iterable) => Iterable + >( + isEquivalent: (self: Iterable.Infer, that: Iterable.Infer) => boolean + ): (self: S) => Iterable.With> + (self: NonEmptyIterable, isEquivalent: (self: A, that: A) => boolean): NonEmptyIterable (self: Iterable, isEquivalent: (self: A, that: A) => boolean): Iterable } = dual(2, (self: Iterable, isEquivalent: (self: A, that: A) => boolean): Iterable => ({ [Symbol.iterator]() { @@ -1051,7 +1110,10 @@ export const dedupeAdjacentWith: { * * @since 2.0.0 */ -export const dedupeAdjacent: (self: Iterable) => Iterable = dedupeAdjacentWith(Equal.equivalence()) +export const dedupe = >( + self: S +): S extends NonEmptyIterable ? NonEmptyIterable : S extends Iterable ? Iterable : never => + dedupeAdjacentWith(self, Equal.equivalence()) as any /** * Zips this Iterable crosswise with the specified Iterable using the specified combiner. @@ -1060,7 +1122,10 @@ export const dedupeAdjacent: (self: Iterable) => Iterable = dedupeAdjac * @category elements */ export const cartesianWith: { - (that: Iterable, f: (a: A, b: B) => C): (self: Iterable) => Iterable + , T extends Iterable, C>( + that: T, f: (a: Iterable.Infer, b: Iterable.Infer) => C + ): (self: S) => Iterable.AndNonEmpty + (self: NonEmptyIterable, that: NonEmptyIterable, f: (a: A, b: B) => C): NonEmptyIterable (self: Iterable, that: Iterable, f: (a: A, b: B) => C): Iterable } = dual( 3, @@ -1075,7 +1140,10 @@ export const cartesianWith: { * @category elements */ export const cartesian: { - (that: Iterable): (self: Iterable) => Iterable<[A, B]> + , T extends Iterable, C>( + that: T + ): (self: S) => Iterable.AndNonEmpty, Iterable.Infer]> + (self: NonEmptyIterable, that: NonEmptyIterable): NonEmptyIterable<[A, B]> (self: Iterable, that: Iterable): Iterable<[A, B]> } = dual( 2,