From b71a988d7f024c304f26aba6f0bfc99481d3c09c Mon Sep 17 00:00:00 2001 From: delacry <45132928+delacry@users.noreply.github.com> Date: Sat, 25 Jul 2026 22:58:48 +0200 Subject: [PATCH] Sync API docs with the narrowed return types --- docs/collection/api/collection.md | 22 ++++++++++----------- docs/collection/api/map.md | 8 ++++---- tests/Type/Collection/TransformTypeTest.php | 4 +++- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/docs/collection/api/collection.md b/docs/collection/api/collection.md index 00bed89..3eb0e44 100644 --- a/docs/collection/api/collection.md +++ b/docs/collection/api/collection.md @@ -101,7 +101,7 @@ count(): int Returns the number of elements in the collection. ```php -countWhere(Closure $predicate): int +countWhere(Closure $predicate): int<0, max> ``` Returns the number of elements matching the predicate `(E, int): bool`. @@ -135,7 +135,7 @@ Reduce, or `null` if empty. ```php sum(?Closure $selector = null): int|float ``` -Sum of all elements, or values returned by the selector `(E, int): int|float`. +Sum of all elements, or values returned by the selector `(E, int): int|float`. The return type narrows to `int` when every summed value is an `int` — a `Collection`, or a selector declared to return `int` — and stays `int|float` otherwise. ```php avg(?Closure $selector = null): float @@ -168,22 +168,22 @@ maxOrNull(?Closure $selector = null): E|null Maximum element, or `null` if empty. Optional selector `(E, int): mixed`. ```php -minOf(Closure $selector): mixed +minOf(Closure $selector): R ``` -Returns the minimum value produced by the selector `(E, int): mixed`. Unlike `min()`, returns the **selector value** itself, not the element. Throws `NoSuchElementException` if empty. +Returns the minimum value produced by the selector `(E, int): R`. Unlike `min()`, returns the **selector value** itself, not the element. The return type follows the selector — one declared `: int` yields `int`. Throws `NoSuchElementException` if empty. ```php -minOfOrNull(Closure $selector): mixed +minOfOrNull(Closure $selector): R|null ``` Returns the minimum selector value, or `null` if empty. ```php -maxOf(Closure $selector): mixed +maxOf(Closure $selector): R ``` -Returns the maximum value produced by the selector `(E, int): mixed`. Unlike `max()`, returns the **selector value** itself, not the element. Throws `NoSuchElementException` if empty. +Returns the maximum value produced by the selector `(E, int): R`. Unlike `max()`, returns the **selector value** itself, not the element. The return type follows the selector — one declared `: int` yields `int`. Throws `NoSuchElementException` if empty. ```php -maxOfOrNull(Closure $selector): mixed +maxOfOrNull(Closure $selector): R|null ``` Returns the maximum selector value, or `null` if empty. @@ -216,7 +216,7 @@ Filter by predicate `(E, int): bool`. Returns `ImmutableList` for lists, `Immuta ```php filterNotNull(): Collection ``` -Filter out `null` elements. +Filter out `null` elements. The element type narrows to exclude `null` — `Collection` becomes `Collection`. ```php filterInstanceOf(string $type): Collection @@ -239,9 +239,9 @@ flatMap(Closure $transform): Collection Transform and flatten. Closure: `(E, int): iterable`. ```php -flatten(): Collection +flatten(): Collection ``` -Flatten a collection of iterables. +Flatten a collection of iterables, one level deep. Iterable elements contribute their own values (`V` is the element type of `E`); non-iterable elements are kept as-is. So `Collection>` becomes `Collection`, `Collection|string>` becomes `Collection`, and `Collection` stays `Collection`. Only one level is removed — flattening `Collection>>` yields `Collection>`. ```php takeFirst(int $n = 1): Collection diff --git a/docs/collection/api/map.md b/docs/collection/api/map.md index 2bca1c1..e01d297 100644 --- a/docs/collection/api/map.md +++ b/docs/collection/api/map.md @@ -99,7 +99,7 @@ count(): int Returns the number of entries in the map. ```php -countWhere(Closure $predicate): int +countWhere(Closure $predicate): int<0, max> ``` Returns the number of entries matching the predicate `(V, K): bool`. @@ -135,7 +135,7 @@ Filter by value predicate `(V): bool`. ```php filterValuesNotNull(): ImmutableMap ``` -Exclude entries with `null` values. +Exclude entries with `null` values. The value type narrows to exclude `null` — `Map` becomes `Map`. Keys are preserved. ```php filterValuesInstanceOf(string $type): ImmutableMap @@ -235,9 +235,9 @@ flatMap(Closure $transform): ImmutableList Transform each entry with `(V, K): iterable` and flatten all results into a single list. ```php -toArray(KeyCollisionStrategy $onCollision = KeyCollisionStrategy::Throw): array +toArray(KeyCollisionStrategy $onCollision = KeyCollisionStrategy::Throw): array ``` -Convert to PHP array. Only works with scalar keys. Throws `ConversionException` for object keys or key collisions. +Convert to PHP array. Only works with scalar keys. Throws `ConversionException` for object keys or key collisions. The key type is preserved when `K` is a valid array key — a `Map` yields `array`; a map with keys PHP cannot use natively falls back to `array`. ```php toPairs(): list diff --git a/tests/Type/Collection/TransformTypeTest.php b/tests/Type/Collection/TransformTypeTest.php index 1a581a8..b3b2cd1 100644 --- a/tests/Type/Collection/TransformTypeTest.php +++ b/tests/Type/Collection/TransformTypeTest.php @@ -30,7 +30,9 @@ assertType('Noctud\Collection\Collection', $c->map(fn (string $x): int => (int) $x)); assertType('Noctud\Collection\Collection', $c->mapNotNull(fn (string $x): ?int => $x !== '' ? 1 : null)); assertType('Noctud\Collection\Collection', $c->flatMap(fn (string $x): array => [(int) $x])); -assertType('Noctud\Collection\Collection', $c->flatten()); + +// flatten() keeps non-iterable elements as-is; see FlattenTypeTest for the nesting cases. +assertType('Noctud\Collection\Collection', $c->flatten()); // Slicing preserves the element type. assertType('Noctud\Collection\Collection', $c->takeFirst(2));