From 3ad942906726be93cdeb44d844807b217d247a8f Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 11 Aug 2021 19:16:16 +0100 Subject: [PATCH 01/23] Adds CI workflow --- .github/workflows/types.yml | 45 +++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 .github/workflows/types.yml diff --git a/.github/workflows/types.yml b/.github/workflows/types.yml new file mode 100644 index 000000000000..2b8d7f717ed3 --- /dev/null +++ b/.github/workflows/types.yml @@ -0,0 +1,45 @@ +name: types + +on: + push: + pull_request: + schedule: + - cron: '0 0 * * *' + +jobs: + linux_tests: + runs-on: ubuntu-20.04 + + strategy: + fail-fast: true + matrix: + php: ['8.0'] + stability: [prefer-lowest, prefer-stable] + include: + - php: '8.1' + flags: "--ignore-platform-req=php" + stability: prefer-stable + + name: PHP ${{ matrix.php }} - ${{ matrix.stability }} + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + tools: composer:v2 + coverage: none + + - name: Install dependencies + uses: nick-invision/retry@v1 + with: + timeout_minutes: 5 + max_attempts: 5 + command: composer update --${{ matrix.stability }} --prefer-dist --no-interaction --no-progress ${{ matrix.flags }} + + - name: Execute type checking + continue-on-error: ${{ matrix.php > 8 }} + run: vendor/bin/phpstan From b13559ba29f7738e91a029c4d98c439b7e1d38c9 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 11 Aug 2021 19:16:31 +0100 Subject: [PATCH 02/23] Adds phpstan --- composer.json | 1 + phpstan.neon.dist | 4 ++++ 2 files changed, 5 insertions(+) create mode 100644 phpstan.neon.dist diff --git a/composer.json b/composer.json index 6032d975078a..72ade6dee3a8 100644 --- a/composer.json +++ b/composer.json @@ -88,6 +88,7 @@ "mockery/mockery": "^1.4.3", "orchestra/testbench-core": "^7.0", "pda/pheanstalk": "^4.0", + "phpstan/phpstan": "^0.12.94", "phpunit/phpunit": "^9.4", "predis/predis": "^1.1.2", "symfony/cache": "^5.3" diff --git a/phpstan.neon.dist b/phpstan.neon.dist new file mode 100644 index 000000000000..609d438c7593 --- /dev/null +++ b/phpstan.neon.dist @@ -0,0 +1,4 @@ +parameters: + paths: + - types + level: max From 4a17695436ec0895ad9e75370eaf46e730181bce Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 11 Aug 2021 19:17:11 +0100 Subject: [PATCH 03/23] Adds work in progress regarding generic collections --- src/Illuminate/Collections/Collection.php | 70 +++++--- src/Illuminate/Collections/Enumerable.php | 88 ++++++---- .../Collections/Traits/EnumeratesValues.php | 53 +++--- src/Illuminate/Collections/helpers.php | 6 +- .../Contracts/Support/Arrayable.php | 6 +- types/Support/Collection.php | 158 ++++++++++++++++++ 6 files changed, 296 insertions(+), 85 deletions(-) create mode 100644 types/Support/Collection.php diff --git a/src/Illuminate/Collections/Collection.php b/src/Illuminate/Collections/Collection.php index cbde79e64eb5..5277833d3390 100644 --- a/src/Illuminate/Collections/Collection.php +++ b/src/Illuminate/Collections/Collection.php @@ -8,21 +8,30 @@ use Illuminate\Support\Traits\Macroable; use stdClass; +/** + * @template TKey as array-key + * @template TValue + * @implements \ArrayAccess + * @implements \Illuminate\Support\Enumerable + */ class Collection implements ArrayAccess, Enumerable { + /** + * @use \Illuminate\Support\Traits\EnumeratesValues + */ use EnumeratesValues, Macroable; /** * The items contained in the collection. * - * @var array + * @var array */ protected $items = []; /** * Create a new collection. * - * @param mixed $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable|null $items * @return void */ public function __construct($items = []) @@ -35,7 +44,7 @@ public function __construct($items = []) * * @param int $from * @param int $to - * @return static + * @return static */ public static function range($from, $to) { @@ -45,7 +54,7 @@ public static function range($from, $to) /** * Get all of the items in the collection. * - * @return array + * @return array */ public function all() { @@ -65,8 +74,8 @@ public function lazy() /** * Get the average value of a given key. * - * @param callable|string|null $callback - * @return mixed + * @param (callable(TValue): float|int)|string|null $callback + * @return float|int|null */ public function avg($callback = null) { @@ -86,8 +95,8 @@ public function avg($callback = null) /** * Get the median of a given key. * - * @param string|array|null $key - * @return mixed + * @param string|array|null $key + * @return float|int|null */ public function median($key = null) { @@ -116,8 +125,8 @@ public function median($key = null) /** * Get the mode of a given key. * - * @param string|array|null $key - * @return array|null + * @param string|array|null $key + * @return array|null */ public function mode($key = null) { @@ -145,7 +154,7 @@ public function mode($key = null) /** * Collapse the collection of items into a single array. * - * @return static + * @return static */ public function collapse() { @@ -155,9 +164,9 @@ public function collapse() /** * Determine if an item exists in the collection. * - * @param mixed $key - * @param mixed $operator - * @param mixed $value + * @param (callable(TValue): bool)|TValue|string $key + * @param TValue|string|null $operator + * @param TValue|null $value * @return bool */ public function contains($key, $operator = null, $value = null) @@ -178,8 +187,11 @@ public function contains($key, $operator = null, $value = null) /** * Cross join with the given lists, returning all possible permutations. * - * @param mixed ...$lists - * @return static + * @template TCrossJoinKey + * @template TCrossJoinValue + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable ...$lists + * @return static> */ public function crossJoin(...$lists) { @@ -191,8 +203,8 @@ public function crossJoin(...$lists) /** * Get the items in the collection that are not present in the given items. * - * @param mixed $items - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static */ public function diff($items) { @@ -202,9 +214,9 @@ public function diff($items) /** * Get the items in the collection that are not present in the given items, using the callback. * - * @param mixed $items - * @param callable $callback - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @param callable(TValue): int $callback + * @return static */ public function diffUsing($items, callable $callback) { @@ -350,9 +362,11 @@ public function filter(callable $callback = null) /** * Get the first item from the collection passing the given truth test. * - * @param callable|null $callback - * @param mixed $default - * @return mixed + * @template TFirstDefaultValue + * + * @param (callable(TValue): bool)|null $callback + * @param TFirstDefaultValue $default + * @return TValue|TFirstDefaultValue */ public function first(callable $callback = null, $default = null) { @@ -398,9 +412,11 @@ public function forget($keys) /** * Get an item from the collection by key. * - * @param mixed $key - * @param mixed $default - * @return mixed + * @template TGetDefault + * + * @param TKey $key + * @param TGetDefault $default + * @return TValue|TGetDefault */ public function get($key, $default = null) { diff --git a/src/Illuminate/Collections/Enumerable.php b/src/Illuminate/Collections/Enumerable.php index 59109e719e0f..869283947ddc 100644 --- a/src/Illuminate/Collections/Enumerable.php +++ b/src/Illuminate/Collections/Enumerable.php @@ -8,13 +8,22 @@ use IteratorAggregate; use JsonSerializable; +/** + * @template TKey as array-key + * @template TValue + * @extends \Illuminate\Contracts\Support\Arrayable + * @extends \IteratorAggregate + */ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, JsonSerializable { /** * Create a new collection instance if the value isn't one already. * - * @param mixed $items - * @return static + * @template TMakeKey as array-key + * @template TMakeValue + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable|null $items + * @return static */ public static function make($items = []); @@ -39,23 +48,29 @@ public static function range($from, $to); /** * Wrap the given value in a collection if applicable. * - * @param mixed $value - * @return static + * @template TWrapKey as array-key + * @template TWrapValue + * + * @param iterable $value + * @return static */ public static function wrap($value); /** * Get the underlying items from the given collection if applicable. * - * @param array|static $value - * @return array + * @template TUnwrapKey as array-key + * @template TUnwrapValue + * + * @param array|static $value + * @return array */ public static function unwrap($value); /** * Create a new instance with no items. * - * @return static + * @return static */ public static function empty(); @@ -69,40 +84,40 @@ public function all(); /** * Alias for the "avg" method. * - * @param callable|string|null $callback - * @return mixed + * @param (callable(TValue): float|int)|string|null $callback + * @return float|int|null */ public function average($callback = null); /** * Get the median of a given key. * - * @param string|array|null $key - * @return mixed + * @param string|array|null $key + * @return float|int|null */ public function median($key = null); /** * Get the mode of a given key. * - * @param string|array|null $key - * @return array|null + * @param string|array|null $key + * @return array|null */ public function mode($key = null); /** * Collapse the items into a single enumerable. * - * @return static + * @return static */ public function collapse(); /** * Alias for the "contains" method. * - * @param mixed $key - * @param mixed $operator - * @param mixed $value + * @param (callable(TValue): bool)|TValue|string $key + * @param TValue|string|null $operator + * @param TValue|null $value * @return bool */ public function some($key, $operator = null, $value = null); @@ -110,8 +125,8 @@ public function some($key, $operator = null, $value = null); /** * Determine if an item exists, using strict comparison. * - * @param mixed $key - * @param mixed $value + * @param (callable(TValue): bool)|TValue|string $key + * @param TValue|null $value * @return bool */ public function containsStrict($key, $value = null); @@ -119,17 +134,17 @@ public function containsStrict($key, $value = null); /** * Get the average value of a given key. * - * @param callable|string|null $callback - * @return mixed + * @param (callable(TValue): float|int)|string|null $callback + * @return float|int|null */ public function avg($callback = null); /** * Determine if an item exists in the enumerable. * - * @param mixed $key - * @param mixed $operator - * @param mixed $value + * @param (callable(TValue): bool)|TValue|string $key + * @param TValue|string|null $operator + * @param TValue|null $value * @return bool */ public function contains($key, $operator = null, $value = null); @@ -137,8 +152,11 @@ public function contains($key, $operator = null, $value = null); /** * Cross join with the given lists, returning all possible permutations. * - * @param mixed ...$lists - * @return static + * @template TCrossJoinKey + * @template TCrossJoinValue + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable ...$lists + * @return static> */ public function crossJoin(...$lists); @@ -146,7 +164,7 @@ public function crossJoin(...$lists); * Dump the collection and end the script. * * @param mixed ...$args - * @return void + * @return never */ public function dd(...$args); @@ -160,17 +178,17 @@ public function dump(); /** * Get the items that are not present in the given items. * - * @param mixed $items - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static */ public function diff($items); /** * Get the items that are not present in the given items, using the callback. * - * @param mixed $items - * @param callable $callback - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @param callable(TValue): int $callback + * @return static */ public function diffUsing($items, callable $callback); @@ -459,9 +477,9 @@ public function flip(); /** * Get an item from the collection by key. * - * @param mixed $key - * @param mixed $default - * @return mixed + * @param TKey $key + * @param TGetDefault $default + * @return TValue|TGetDefault */ public function get($key, $default = null); diff --git a/src/Illuminate/Collections/Traits/EnumeratesValues.php b/src/Illuminate/Collections/Traits/EnumeratesValues.php index 530bfe3bd321..0e67752583e4 100644 --- a/src/Illuminate/Collections/Traits/EnumeratesValues.php +++ b/src/Illuminate/Collections/Traits/EnumeratesValues.php @@ -43,6 +43,8 @@ * @property-read HigherOrderCollectionProxy $unless * @property-read HigherOrderCollectionProxy $until * @property-read HigherOrderCollectionProxy $when + * @template TKey as array-key + * @template TValue */ trait EnumeratesValues { @@ -51,7 +53,7 @@ trait EnumeratesValues /** * The methods that can be proxied. * - * @var string[] + * @var array */ protected static $proxies = [ 'average', @@ -86,8 +88,11 @@ trait EnumeratesValues /** * Create a new collection instance if the value isn't one already. * - * @param mixed $items - * @return static + * @template TMakeKey as array-key + * @template TMakeValue + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable|null $items + * @return static */ public static function make($items = []) { @@ -97,8 +102,11 @@ public static function make($items = []) /** * Wrap the given value in a collection if applicable. * - * @param mixed $value - * @return static + * @template TWrapKey as array-key + * @template TWrapValue + * + * @param iterable $value + * @return static */ public static function wrap($value) { @@ -110,8 +118,11 @@ public static function wrap($value) /** * Get the underlying items from the given collection if applicable. * - * @param array|static $value - * @return array + * @template TUnwrapKey as array-key + * @template TUnwrapValue + * + * @param array|static $value + * @return array */ public static function unwrap($value) { @@ -121,7 +132,7 @@ public static function unwrap($value) /** * Create a new instance with no items. * - * @return static + * @return static */ public static function empty() { @@ -131,9 +142,11 @@ public static function empty() /** * Create a new collection by invoking the callback a given amount of times. * + * @template TTimesValue + * * @param int $number - * @param callable|null $callback - * @return static + * @param (callable(int): TTimesValue)|null $callback + * @return static */ public static function times($number, callable $callback = null) { @@ -149,8 +162,8 @@ public static function times($number, callable $callback = null) /** * Alias for the "avg" method. * - * @param callable|string|null $callback - * @return mixed + * @param (callable(TValue): float|int)|string|null $callback + * @return float|int|null */ public function average($callback = null) { @@ -160,9 +173,9 @@ public function average($callback = null) /** * Alias for the "contains" method. * - * @param mixed $key - * @param mixed $operator - * @param mixed $value + * @param (callable(TValue): bool)|TValue|string $key + * @param TValue|string|null $operator + * @param TValue|null $value * @return bool */ public function some($key, $operator = null, $value = null) @@ -173,8 +186,8 @@ public function some($key, $operator = null, $value = null) /** * Determine if an item exists, using strict comparison. * - * @param mixed $key - * @param mixed $value + * @param (callable(TValue): bool)|TValue|string $key + * @param TValue|null $value * @return bool */ public function containsStrict($key, $value = null) @@ -202,7 +215,7 @@ public function containsStrict($key, $value = null) * Dump the items and end the script. * * @param mixed ...$args - * @return void + * @return never */ public function dd(...$args) { @@ -230,7 +243,7 @@ public function dump() /** * Execute a callback over each item. * - * @param callable $callback + * @param callable(TValue): mixed $callback * @return $this */ public function each(callable $callback) @@ -788,7 +801,7 @@ public function collect() /** * Get the collection of items as a plain array. * - * @return array + * @return array */ public function toArray() { diff --git a/src/Illuminate/Collections/helpers.php b/src/Illuminate/Collections/helpers.php index 5138b2cd1a12..0037540b7d1e 100644 --- a/src/Illuminate/Collections/helpers.php +++ b/src/Illuminate/Collections/helpers.php @@ -7,8 +7,10 @@ /** * Create a collection from the given value. * - * @param mixed $value - * @return \Illuminate\Support\Collection + * @template TKey as array-key + * @template TValue + * @param \Illuminate\Contracts\Support\Arrayable|iterable|null $value + * @return \Illuminate\Support\Collection */ function collect($value = null) { diff --git a/src/Illuminate/Contracts/Support/Arrayable.php b/src/Illuminate/Contracts/Support/Arrayable.php index 5ad93b70bd3b..5a007c4fa00e 100755 --- a/src/Illuminate/Contracts/Support/Arrayable.php +++ b/src/Illuminate/Contracts/Support/Arrayable.php @@ -2,12 +2,16 @@ namespace Illuminate\Contracts\Support; +/** + * @template TKey as array-key + * @template TValue + */ interface Arrayable { /** * Get the instance as an array. * - * @return array + * @return array */ public function toArray(); } diff --git a/types/Support/Collection.php b/types/Support/Collection.php new file mode 100644 index 000000000000..d30395986efd --- /dev/null +++ b/types/Support/Collection.php @@ -0,0 +1,158 @@ + $arrayable */ +$arrayable = []; +/** @var iterable $iterable */ +$iterable = []; +/** @var Traversable $traversable */ +$traversable = []; + +assertType('Illuminate\Support\Collection', $collection); + +foreach ($collection as $int => $user) { + assertType('int', $int); + assertType('User', $user); +} + +assertType('Illuminate\Support\Collection', collect(['string'])); +assertType('Illuminate\Support\Collection', collect(['string' => new User])); +assertType('Illuminate\Support\Collection', collect($arrayable)); +assertType('Illuminate\Support\Collection', collect($collection)); +assertType('Illuminate\Support\Collection', collect($collection)); +assertType('Illuminate\Support\Collection', collect($iterable)); +assertType('Illuminate\Support\Collection', collect($traversable)); + +assertType('Illuminate\Support\Collection', $collection::make(['string'])); +assertType('Illuminate\Support\Collection', $collection::make(['string' => new User])); +assertType('Illuminate\Support\Collection', $collection::make($arrayable)); +assertType('Illuminate\Support\Collection', $collection::make($collection)); +assertType('Illuminate\Support\Collection', $collection::make($collection)); +assertType('Illuminate\Support\Collection', $collection::make($iterable)); +assertType('Illuminate\Support\Collection', $collection::make($traversable)); + +assertType('Illuminate\Support\Collection', $collection::times(10, function ($int) { + // assertType('int', $int); + + return new User; +})); + +assertType('Illuminate\Support\Collection', $collection->each(function ($user) { + assertType('User', $user); +})); + +assertType('Illuminate\Support\Collection', $collection->range(1, 100)); + +assertType('Illuminate\Support\Collection', $collection->wrap(['string'])); +assertType('Illuminate\Support\Collection', $collection->wrap(['string' => new User])); + +assertType('array', $collection->unwrap(['string'])); +assertType('array', $collection->unwrap( + $collection +)); + +assertType('Illuminate\Support\Collection', $collection::empty()); + +assertType('float|int|null', $collection->average()); +assertType('float|int|null', $collection->average('string')); +assertType('float|int|null', $collection->average(function ($user) { + assertType('User', $user); + + return 1; +})); +assertType('float|int|null', $collection->average(function ($user) { + assertType('User', $user); + + return 0.1; +})); + +assertType('float|int|null', $collection->median()); +assertType('float|int|null', $collection->median('string')); +assertType('float|int|null', $collection->median(['string'])); + +assertType('array|null', $collection->mode()); +assertType('array|null', $collection->mode('string')); +assertType('array|null', $collection->mode(['string'])); + +assertType('Illuminate\Support\Collection', $collection->collapse()); + +assertType('bool', $collection->some(function ($user) { + assertType('User', $user); + + return true; +})); +assertType('bool', $collection::make(['string'])->some('string', '=', 'string')); + +assertType('bool', $collection->containsStrict(function ($user) { + assertType('User', $user); + + return true; +})); +assertType('bool', $collection::make(['string'])->containsStrict('string', 'string')); + +assertType('float|int|null', $collection->avg()); +assertType('float|int|null', $collection->avg('string')); +assertType('float|int|null', $collection->avg(function ($user) { + assertType('User', $user); + + return 1; +})); +assertType('float|int|null', $collection->avg(function ($user) { + assertType('User', $user); + + return 0.1; +})); + +assertType('bool', $collection->contains(function ($user) { + assertType('User', $user); + + return true; +})); +assertType('bool', $collection::make(['string'])->contains('string', '=', 'string')); + +assertType('Illuminate\Support\Collection>', $collection->crossJoin($collection::make(['string']))); +assertType('Illuminate\Support\Collection>', $collection->crossJoin([1, 2])); + +assertType('Illuminate\Support\Collection', $collection::make([3, 4])->diff([1, 2])); +assertType('Illuminate\Support\Collection', $collection::make(['string-1'])->diff(['string-2'])); + +assertType('Illuminate\Support\Collection', $collection::make([3, 4])->diffUsing([1, 2], function ($int) { + assertType('int', $int); + + return -1; +})); +assertType('Illuminate\Support\Collection', $collection::make(['string-1'])->diffUsing(['string-2'], function ($string) { + assertType('string', $string); + + return -1; +})); + +assertType('array', $collection->all()); + +assertType('User|null', $collection->get(0)); +assertType('string|User', $collection->get(0, 'string')); + +assertType('User|null', $collection->first()); +assertType('User|null', $collection->first(function ($user) { + assertType('User', $user); + + return true; +})); +assertType('string|User', $collection->first(function ($user) { + assertType('User', $user); + + return false; +}, 'string')); + +assertType('array', $collection->toArray()); +assertType('array', collect(['string' => 'string'])->toArray()); +assertType('array', collect([1, 2])->toArray()); From 0d92200ca3f56ca2b806581f60db99f6660da265 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 11 Aug 2021 20:02:45 +0100 Subject: [PATCH 04/23] Fixes missing template --- src/Illuminate/Collections/Enumerable.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Illuminate/Collections/Enumerable.php b/src/Illuminate/Collections/Enumerable.php index 869283947ddc..13b4f2392a4d 100644 --- a/src/Illuminate/Collections/Enumerable.php +++ b/src/Illuminate/Collections/Enumerable.php @@ -477,6 +477,8 @@ public function flip(); /** * Get an item from the collection by key. * + * @template TGetDefault + * * @param TKey $key * @param TGetDefault $default * @return TValue|TGetDefault From c47728b3e10744af022a45fc3a62d82872b3fb5e Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 11 Aug 2021 20:03:00 +0100 Subject: [PATCH 05/23] Renames template --- src/Illuminate/Collections/Collection.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Illuminate/Collections/Collection.php b/src/Illuminate/Collections/Collection.php index 5277833d3390..2b5ac0ed7bd1 100644 --- a/src/Illuminate/Collections/Collection.php +++ b/src/Illuminate/Collections/Collection.php @@ -362,11 +362,11 @@ public function filter(callable $callback = null) /** * Get the first item from the collection passing the given truth test. * - * @template TFirstDefaultValue + * @template TFirstDefault * * @param (callable(TValue): bool)|null $callback - * @param TFirstDefaultValue $default - * @return TValue|TFirstDefaultValue + * @param TFirstDefault $default + * @return TValue|TFirstDefault */ public function first(callable $callback = null, $default = null) { From d56156cdc277c327bba40e2989bc61276bbc253a Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 11 Aug 2021 20:03:08 +0100 Subject: [PATCH 06/23] Updates test --- types/Support/Collection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/Support/Collection.php b/types/Support/Collection.php index d30395986efd..cba221cc0a29 100644 --- a/types/Support/Collection.php +++ b/types/Support/Collection.php @@ -9,7 +9,7 @@ class User extends Authenticatable { } -$collection = Collection::make([new User]); +$collection = collect([new User]); /** @var Arrayable $arrayable */ $arrayable = []; /** @var iterable $iterable */ From ab0058abe306eff7d75845d0cd4b1cd153c8c8b1 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 11 Aug 2021 19:03:32 +0000 Subject: [PATCH 07/23] Apply fixes from StyleCI --- types/Support/Collection.php | 1 - 1 file changed, 1 deletion(-) diff --git a/types/Support/Collection.php b/types/Support/Collection.php index cba221cc0a29..e6b683cc36f7 100644 --- a/types/Support/Collection.php +++ b/types/Support/Collection.php @@ -2,7 +2,6 @@ use Illuminate\Contracts\Support\Arrayable; use Illuminate\Foundation\Auth\User as Authenticatable; -use Illuminate\Support\Collection; use function PHPStan\Testing\assertType; class User extends Authenticatable From 97b71acacffd1a7fb956d3ecfe6653194e436684 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Thu, 12 Aug 2021 17:38:40 +0100 Subject: [PATCH 08/23] Adds work in progress regarding generic collections --- src/Illuminate/Collections/Collection.php | 80 ++--- src/Illuminate/Collections/Enumerable.php | 188 ++++++----- .../Collections/Traits/EnumeratesValues.php | 89 +++--- src/Illuminate/Collections/helpers.php | 2 +- .../Conditionable/Traits/Conditionable.php | 20 +- .../Contracts/Support/Arrayable.php | 2 +- types/Support/Collection.php | 295 +++++++++++++++++- 7 files changed, 499 insertions(+), 177 deletions(-) diff --git a/src/Illuminate/Collections/Collection.php b/src/Illuminate/Collections/Collection.php index 2b5ac0ed7bd1..5f51041d51a8 100644 --- a/src/Illuminate/Collections/Collection.php +++ b/src/Illuminate/Collections/Collection.php @@ -9,7 +9,7 @@ use stdClass; /** - * @template TKey as array-key + * @template TKey of array-key * @template TValue * @implements \ArrayAccess * @implements \Illuminate\Support\Enumerable @@ -226,8 +226,8 @@ public function diffUsing($items, callable $callback) /** * Get the items in the collection whose keys and values are not present in the given items. * - * @param mixed $items - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static */ public function diffAssoc($items) { @@ -237,9 +237,9 @@ public function diffAssoc($items) /** * Get the items in the collection whose keys and values are not present in the given items, using the callback. * - * @param mixed $items - * @param callable $callback - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @param callable(TKey): int $callback + * @return static */ public function diffAssocUsing($items, callable $callback) { @@ -249,8 +249,8 @@ public function diffAssocUsing($items, callable $callback) /** * Get the items in the collection whose keys are not present in the given items. * - * @param mixed $items - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static */ public function diffKeys($items) { @@ -260,9 +260,9 @@ public function diffKeys($items) /** * Get the items in the collection whose keys are not present in the given items, using the callback. * - * @param mixed $items - * @param callable $callback - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @param callable(TKey): int $callback + * @return static */ public function diffKeysUsing($items, callable $callback) { @@ -272,9 +272,9 @@ public function diffKeysUsing($items, callable $callback) /** * Retrieve duplicate items from the collection. * - * @param callable|string|null $callback + * @param (callable(TValue): bool)|string|null $callback * @param bool $strict - * @return static + * @return static */ public function duplicates($callback = null, $strict = false) { @@ -300,8 +300,8 @@ public function duplicates($callback = null, $strict = false) /** * Retrieve duplicate items from the collection using strict comparison. * - * @param callable|string|null $callback - * @return static + * @param (callable(TValue): bool)|string|null $callback + * @return static */ public function duplicatesStrict($callback = null) { @@ -312,7 +312,7 @@ public function duplicatesStrict($callback = null) * Get the comparison function to detect duplicates. * * @param bool $strict - * @return \Closure + * @return callable(TValue, TValue): bool */ protected function duplicateComparator($strict) { @@ -330,8 +330,8 @@ protected function duplicateComparator($strict) /** * Get all items except for those with the specified keys. * - * @param \Illuminate\Support\Collection|mixed $keys - * @return static + * @param \Illuminate\Support\Enumerable|array $keys + * @return static */ public function except($keys) { @@ -347,8 +347,8 @@ public function except($keys) /** * Run a filter over each of the items. * - * @param callable|null $callback - * @return static + * @param (callable(TValue): bool)|null $callback + * @return static */ public function filter(callable $callback = null) { @@ -377,7 +377,7 @@ public function first(callable $callback = null, $default = null) * Get a flattened array of the items in the collection. * * @param int $depth - * @return static + * @return static */ public function flatten($depth = INF) { @@ -387,7 +387,7 @@ public function flatten($depth = INF) /** * Flip the items in the collection. * - * @return static + * @return static */ public function flip() { @@ -430,9 +430,9 @@ public function get($key, $default = null) /** * Group an associative array by a field or using a callback. * - * @param array|callable|string $groupBy + * @param (callable(TValue, TKey): array-key)|array|string $groupBy * @param bool $preserveKeys - * @return static + * @return static> */ public function groupBy($groupBy, $preserveKeys = false) { @@ -476,8 +476,8 @@ public function groupBy($groupBy, $preserveKeys = false) /** * Key an associative array by a field or using a callback. * - * @param callable|string $keyBy - * @return static + * @param (callable(TValue, TKey): array-key)|array|string $keyBy + * @return static> */ public function keyBy($keyBy) { @@ -501,7 +501,7 @@ public function keyBy($keyBy) /** * Determine if an item exists in the collection by key. * - * @param mixed $key + * @param TKey|array $key * @return bool */ public function has($key) @@ -538,8 +538,8 @@ public function implode($value, $glue = null) /** * Intersect the collection with the given items. * - * @param mixed $items - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static */ public function intersect($items) { @@ -549,8 +549,8 @@ public function intersect($items) /** * Intersect the collection with the given items by key. * - * @param mixed $items - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static */ public function intersectByKeys($items) { @@ -612,7 +612,7 @@ public function join($glue, $finalGlue = '') /** * Get the keys of the collection items. * - * @return static + * @return static */ public function keys() { @@ -622,9 +622,11 @@ public function keys() /** * Get the last item from the collection. * - * @param callable|null $callback - * @param mixed $default - * @return mixed + * @template TLastDefault + * + * @param (callable(TValue, TKey): bool)|null $callback + * @param TLastDefault $default + * @return TValue|TLastDefault */ public function last(callable $callback = null, $default = null) { @@ -646,8 +648,10 @@ public function pluck($value, $key = null) /** * Run a map over each of the items. * - * @param callable $callback - * @return static + * @template TMapValue + * + * @param callable(TValue, TKey): TMapValue $callback + * @return static */ public function map(callable $callback) { @@ -843,7 +847,7 @@ public function prepend($value, $key = null) /** * Push one or more items onto the end of the collection. * - * @param mixed $values [optional] + * @param ...TValue $values * @return $this */ public function push(...$values) diff --git a/src/Illuminate/Collections/Enumerable.php b/src/Illuminate/Collections/Enumerable.php index 13b4f2392a4d..438452b6bc06 100644 --- a/src/Illuminate/Collections/Enumerable.php +++ b/src/Illuminate/Collections/Enumerable.php @@ -9,7 +9,7 @@ use JsonSerializable; /** - * @template TKey as array-key + * @template TKey of array-key * @template TValue * @extends \Illuminate\Contracts\Support\Arrayable * @extends \IteratorAggregate @@ -19,7 +19,7 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, /** * Create a new collection instance if the value isn't one already. * - * @template TMakeKey as array-key + * @template TMakeKey of array-key * @template TMakeValue * * @param \Illuminate\Contracts\Support\Arrayable|iterable|null $items @@ -48,7 +48,7 @@ public static function range($from, $to); /** * Wrap the given value in a collection if applicable. * - * @template TWrapKey as array-key + * @template TWrapKey of array-key * @template TWrapValue * * @param iterable $value @@ -59,7 +59,7 @@ public static function wrap($value); /** * Get the underlying items from the given collection if applicable. * - * @template TUnwrapKey as array-key + * @template TUnwrapKey of array-key * @template TUnwrapValue * * @param array|static $value @@ -195,58 +195,58 @@ public function diffUsing($items, callable $callback); /** * Get the items whose keys and values are not present in the given items. * - * @param mixed $items - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static */ public function diffAssoc($items); /** * Get the items whose keys and values are not present in the given items, using the callback. * - * @param mixed $items - * @param callable $callback - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @param callable(TKey): int $callback + * @return static */ public function diffAssocUsing($items, callable $callback); /** * Get the items whose keys are not present in the given items. * - * @param mixed $items - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static */ public function diffKeys($items); /** * Get the items whose keys are not present in the given items, using the callback. * - * @param mixed $items - * @param callable $callback - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @param callable(TKey): int $callback + * @return static */ public function diffKeysUsing($items, callable $callback); /** * Retrieve duplicate items. * - * @param callable|string|null $callback + * @param (callable(TValue): bool)|string|null $callback * @param bool $strict - * @return static + * @return static */ public function duplicates($callback = null, $strict = false); /** * Retrieve duplicate items using strict comparison. * - * @param callable|string|null $callback - * @return static + * @param (callable(TValue): bool)|string|null $callback + * @return static */ public function duplicatesStrict($callback = null); /** * Execute a callback over each item. * - * @param callable $callback + * @param callable(TValue): mixed $callback * @return $this */ public function each(callable $callback); @@ -254,17 +254,17 @@ public function each(callable $callback); /** * Execute a callback over each nested chunk of items. * - * @param callable $callback - * @return static + * @param callable(...mixed): mixed $callback + * @return static */ public function eachSpread(callable $callback); /** * Determine if all items pass the given truth test. * - * @param string|callable $key - * @param mixed $operator - * @param mixed $value + * @param (callable(TValue, TKey): bool)|TValue|string $key + * @param TValue|string|null $operator + * @param TValue|null $value * @return bool */ public function every($key, $operator = null, $value = null); @@ -272,72 +272,84 @@ public function every($key, $operator = null, $value = null); /** * Get all items except for those with the specified keys. * - * @param mixed $keys - * @return static + * @param \Illuminate\Support\Enumerable|array $keys + * @return static */ public function except($keys); /** * Run a filter over each of the items. * - * @param callable|null $callback - * @return static + * @param (callable(TValue): bool)|null $callback + * @return static */ public function filter(callable $callback = null); /** * Apply the callback if the given "value" is (or resolves to) truthy. * + * @template TWhenReturnType as null + * * @param bool $value - * @param callable|null $callback - * @param callable|null $default - * @return static|mixed + * @param (callable($this): TWhenReturnType)|null $callback + * @param (callable($this): TWhenReturnType)|null $default + * @return $this|TWhenReturnType */ public function when($value, callable $callback = null, callable $default = null); /** * Apply the callback if the collection is empty. * - * @param callable $callback - * @param callable|null $default - * @return static|mixed + * @template TWhenEmptyReturnType + * + * @param (callable($this): TWhenEmptyReturnType) $callback + * @param (callable($this): TWhenEmptyReturnType)|null $default + * @return $this|TWhenEmptyReturnType */ public function whenEmpty(callable $callback, callable $default = null); /** * Apply the callback if the collection is not empty. * - * @param callable $callback - * @param callable|null $default - * @return static|mixed + * @template TWhenNotEmptyReturnType + * + * @param callable($this): TWhenNotEmptyReturnType $callback + * @param (callable($this): TWhenNotEmptyReturnType)|null $default + * @return $this|TWhenNotEmptyReturnType */ public function whenNotEmpty(callable $callback, callable $default = null); /** * Apply the callback if the given "value" is (or resolves to) truthy. * + * @template TWhenReturnType + * * @param bool $value - * @param callable $callback - * @param callable|null $default - * @return static|mixed + * @param (callable($this): TUnlessReturnType) $callback + * @param (callable($this): TUnlessReturnType)|null $default + * @return $this|TUnlessReturnType */ public function unless($value, callable $callback, callable $default = null); /** * Apply the callback unless the collection is empty. * - * @param callable $callback - * @param callable|null $default - * @return static|mixed + * @template TUnlessEmptyReturnType + * + * @param callable($this): TUnlessEmptyReturnType $callback + * @param (callable($this): TUnlessEmptyReturnType)|null $default + * @return $this|TUnlessEmptyReturnType */ public function unlessEmpty(callable $callback, callable $default = null); /** * Apply the callback unless the collection is not empty. * - * @param callable $callback - * @param callable|null $default - * @return static|mixed + * @template TUnlessNotEmptyReturnType + * + * @param callable($this): TUnlessNotEmptyReturnType $callback + * @param (callable($this): TUnlessNotEmptyReturnType)|null $default + * @return $this|TUnlessNotEmptyReturnType */ public function unlessNotEmpty(callable $callback, callable $default = null); @@ -347,7 +359,7 @@ public function unlessNotEmpty(callable $callback, callable $default = null); * @param string $key * @param mixed $operator * @param mixed $value - * @return static + * @return static */ public function where($key, $operator = null, $value = null); @@ -355,7 +367,7 @@ public function where($key, $operator = null, $value = null); * Filter items where the value for the given key is null. * * @param string|null $key - * @return static + * @return static */ public function whereNull($key = null); @@ -363,7 +375,7 @@ public function whereNull($key = null); * Filter items where the value for the given key is not null. * * @param string|null $key - * @return static + * @return static */ public function whereNotNull($key = null); @@ -372,7 +384,7 @@ public function whereNotNull($key = null); * * @param string $key * @param mixed $value - * @return static + * @return static */ public function whereStrict($key, $value); @@ -380,9 +392,9 @@ public function whereStrict($key, $value); * Filter items by the given key value pair. * * @param string $key - * @param mixed $values + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values * @param bool $strict - * @return static + * @return static */ public function whereIn($key, $values, $strict = false); @@ -390,8 +402,8 @@ public function whereIn($key, $values, $strict = false); * Filter items by the given key value pair using strict comparison. * * @param string $key - * @param mixed $values - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values + * @return static */ public function whereInStrict($key, $values); @@ -399,8 +411,8 @@ public function whereInStrict($key, $values); * Filter items such that the value of the given key is between the given values. * * @param string $key - * @param array $values - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values + * @return static */ public function whereBetween($key, $values); @@ -408,8 +420,8 @@ public function whereBetween($key, $values); * Filter items such that the value of the given key is not between the given values. * * @param string $key - * @param array $values - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values + * @return static */ public function whereNotBetween($key, $values); @@ -417,9 +429,9 @@ public function whereNotBetween($key, $values); * Filter items by the given key value pair. * * @param string $key - * @param mixed $values + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values * @param bool $strict - * @return static + * @return static */ public function whereNotIn($key, $values, $strict = false); @@ -427,25 +439,27 @@ public function whereNotIn($key, $values, $strict = false); * Filter items by the given key value pair using strict comparison. * * @param string $key - * @param mixed $values - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values + * @return static */ public function whereNotInStrict($key, $values); /** * Filter the items, removing any items that don't match the given type(s). * - * @param string|string[] $type - * @return static + * @param class-string|array $type + * @return static */ public function whereInstanceOf($type); /** * Get the first item from the enumerable passing the given truth test. * - * @param callable|null $callback - * @param mixed $default - * @return mixed + * @template TFirstDefault + * + * @param (callable(TValue): bool)|null $callback + * @param TFirstDefault $default + * @return TValue|TFirstDefault */ public function first(callable $callback = null, $default = null); @@ -455,7 +469,7 @@ public function first(callable $callback = null, $default = null); * @param string $key * @param mixed $operator * @param mixed $value - * @return mixed + * @return TValue|null */ public function firstWhere($key, $operator = null, $value = null); @@ -463,14 +477,14 @@ public function firstWhere($key, $operator = null, $value = null); * Get a flattened array of the items in the collection. * * @param int $depth - * @return static + * @return static */ public function flatten($depth = INF); /** * Flip the values with their keys. * - * @return static + * @return static */ public function flip(); @@ -488,24 +502,24 @@ public function get($key, $default = null); /** * Group an associative array by a field or using a callback. * - * @param array|callable|string $groupBy + * @param (callable(TValue, TKey): array-key)|array|string $groupBy * @param bool $preserveKeys - * @return static + * @return static> */ public function groupBy($groupBy, $preserveKeys = false); /** * Key an associative array by a field or using a callback. * - * @param callable|string $keyBy - * @return static + * @param (callable(TValue, TKey): array-key)|array|string $keyBy + * @return static> */ public function keyBy($keyBy); /** * Determine if an item exists in the collection by key. * - * @param mixed $key + * @param TKey|array $key * @return bool */ public function has($key); @@ -522,16 +536,16 @@ public function implode($value, $glue = null); /** * Intersect the collection with the given items. * - * @param mixed $items - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static */ public function intersect($items); /** * Intersect the collection with the given items by key. * - * @param mixed $items - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static */ public function intersectByKeys($items); @@ -561,24 +575,28 @@ public function join($glue, $finalGlue = ''); /** * Get the keys of the collection items. * - * @return static + * @return static */ public function keys(); /** * Get the last item from the collection. * - * @param callable|null $callback - * @param mixed $default - * @return mixed + * @template TLastDefault + * + * @param (callable(TValue, TKey): bool)|null $callback + * @param TLastDefault $default + * @return TValue|TLastDefault */ public function last(callable $callback = null, $default = null); /** * Run a map over each of the items. * - * @param callable $callback - * @return static + * @template TMapValue + * + * @param callable(TValue, TKey): TMapValue $callback + * @return static */ public function map(callable $callback); diff --git a/src/Illuminate/Collections/Traits/EnumeratesValues.php b/src/Illuminate/Collections/Traits/EnumeratesValues.php index 0e67752583e4..45d87b6fbc6c 100644 --- a/src/Illuminate/Collections/Traits/EnumeratesValues.php +++ b/src/Illuminate/Collections/Traits/EnumeratesValues.php @@ -43,7 +43,7 @@ * @property-read HigherOrderCollectionProxy $unless * @property-read HigherOrderCollectionProxy $until * @property-read HigherOrderCollectionProxy $when - * @template TKey as array-key + * @template TKey of array-key * @template TValue */ trait EnumeratesValues @@ -88,7 +88,7 @@ trait EnumeratesValues /** * Create a new collection instance if the value isn't one already. * - * @template TMakeKey as array-key + * @template TMakeKey of array-key * @template TMakeValue * * @param \Illuminate\Contracts\Support\Arrayable|iterable|null $items @@ -102,7 +102,7 @@ public static function make($items = []) /** * Wrap the given value in a collection if applicable. * - * @template TWrapKey as array-key + * @template TWrapKey of array-key * @template TWrapValue * * @param iterable $value @@ -118,7 +118,7 @@ public static function wrap($value) /** * Get the underlying items from the given collection if applicable. * - * @template TUnwrapKey as array-key + * @template TUnwrapKey of array-key * @template TUnwrapValue * * @param array|static $value @@ -260,8 +260,8 @@ public function each(callable $callback) /** * Execute a callback over each nested chunk of items. * - * @param callable $callback - * @return static + * @param callable(...mixed): mixed $callback + * @return static */ public function eachSpread(callable $callback) { @@ -275,9 +275,9 @@ public function eachSpread(callable $callback) /** * Determine if all items pass the given truth test. * - * @param string|callable $key - * @param mixed $operator - * @param mixed $value + * @param (callable(TValue, TKey): bool)|TValue|string $key + * @param TValue|string|null $operator + * @param TValue|null $value * @return bool */ public function every($key, $operator = null, $value = null) @@ -303,7 +303,7 @@ public function every($key, $operator = null, $value = null) * @param string $key * @param mixed $operator * @param mixed $value - * @return mixed + * @return TValue|null */ public function firstWhere($key, $operator = null, $value = null) { @@ -474,9 +474,11 @@ public function sum($callback = null) /** * Apply the callback if the collection is empty. * - * @param callable $callback - * @param callable|null $default - * @return static|mixed + * @template TWhenEmptyReturnType + * + * @param (callable($this): TWhenEmptyReturnType) $callback + * @param (callable($this): TWhenEmptyReturnType)|null $default + * @return $this|TWhenEmptyReturnType */ public function whenEmpty(callable $callback, callable $default = null) { @@ -486,9 +488,11 @@ public function whenEmpty(callable $callback, callable $default = null) /** * Apply the callback if the collection is not empty. * - * @param callable $callback - * @param callable|null $default - * @return static|mixed + * @template TWhenNotEmptyReturnType + * + * @param callable($this): TWhenNotEmptyReturnType $callback + * @param (callable($this): TWhenNotEmptyReturnType)|null $default + * @return $this|TWhenNotEmptyReturnType */ public function whenNotEmpty(callable $callback, callable $default = null) { @@ -498,9 +502,11 @@ public function whenNotEmpty(callable $callback, callable $default = null) /** * Apply the callback unless the collection is empty. * - * @param callable $callback - * @param callable|null $default - * @return static|mixed + * @template TUnlessEmptyReturnType + * + * @param callable($this): TUnlessEmptyReturnType $callback + * @param (callable($this): TUnlessEmptyReturnType)|null $default + * @return $this|TUnlessEmptyReturnType */ public function unlessEmpty(callable $callback, callable $default = null) { @@ -510,9 +516,11 @@ public function unlessEmpty(callable $callback, callable $default = null) /** * Apply the callback unless the collection is not empty. * - * @param callable $callback - * @param callable|null $default - * @return static|mixed + * @template TUnlessNotEmptyReturnType + * + * @param callable($this): TUnlessNotEmptyReturnType $callback + * @param (callable($this): TUnlessNotEmptyReturnType)|null $default + * @return $this|TUnlessNotEmptyReturnType */ public function unlessNotEmpty(callable $callback, callable $default = null) { @@ -525,7 +533,7 @@ public function unlessNotEmpty(callable $callback, callable $default = null) * @param string $key * @param mixed $operator * @param mixed $value - * @return static + * @return static */ public function where($key, $operator = null, $value = null) { @@ -536,7 +544,7 @@ public function where($key, $operator = null, $value = null) * Filter items where the value for the given key is null. * * @param string|null $key - * @return static + * @return static */ public function whereNull($key = null) { @@ -547,7 +555,7 @@ public function whereNull($key = null) * Filter items where the value for the given key is not null. * * @param string|null $key - * @return static + * @return static */ public function whereNotNull($key = null) { @@ -559,7 +567,8 @@ public function whereNotNull($key = null) * * @param string $key * @param mixed $value - * @return static + * @param bool $strict + * @return static */ public function whereStrict($key, $value) { @@ -570,9 +579,9 @@ public function whereStrict($key, $value) * Filter items by the given key value pair. * * @param string $key - * @param mixed $values + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values * @param bool $strict - * @return static + * @return static */ public function whereIn($key, $values, $strict = false) { @@ -587,8 +596,8 @@ public function whereIn($key, $values, $strict = false) * Filter items by the given key value pair using strict comparison. * * @param string $key - * @param mixed $values - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values + * @return static */ public function whereInStrict($key, $values) { @@ -599,8 +608,8 @@ public function whereInStrict($key, $values) * Filter items such that the value of the given key is between the given values. * * @param string $key - * @param array $values - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values + * @return static */ public function whereBetween($key, $values) { @@ -611,8 +620,8 @@ public function whereBetween($key, $values) * Filter items such that the value of the given key is not between the given values. * * @param string $key - * @param array $values - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values + * @return static */ public function whereNotBetween($key, $values) { @@ -625,9 +634,9 @@ public function whereNotBetween($key, $values) * Filter items by the given key value pair. * * @param string $key - * @param mixed $values + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values * @param bool $strict - * @return static + * @return static */ public function whereNotIn($key, $values, $strict = false) { @@ -642,8 +651,8 @@ public function whereNotIn($key, $values, $strict = false) * Filter items by the given key value pair using strict comparison. * * @param string $key - * @param mixed $values - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values + * @return static */ public function whereNotInStrict($key, $values) { @@ -653,8 +662,8 @@ public function whereNotInStrict($key, $values) /** * Filter the items, removing any items that don't match the given type(s). * - * @param string|string[] $type - * @return static + * @param class-string|array $type + * @return static */ public function whereInstanceOf($type) { diff --git a/src/Illuminate/Collections/helpers.php b/src/Illuminate/Collections/helpers.php index 0037540b7d1e..61d3dd5fa91f 100644 --- a/src/Illuminate/Collections/helpers.php +++ b/src/Illuminate/Collections/helpers.php @@ -7,7 +7,7 @@ /** * Create a collection from the given value. * - * @template TKey as array-key + * @template TKey of array-key * @template TValue * @param \Illuminate\Contracts\Support\Arrayable|iterable|null $value * @return \Illuminate\Support\Collection diff --git a/src/Illuminate/Conditionable/Traits/Conditionable.php b/src/Illuminate/Conditionable/Traits/Conditionable.php index 924051fb7260..aa77df52759b 100644 --- a/src/Illuminate/Conditionable/Traits/Conditionable.php +++ b/src/Illuminate/Conditionable/Traits/Conditionable.php @@ -10,10 +10,12 @@ trait Conditionable /** * Apply the callback if the given "value" is (or resolves to) truthy. * - * @param mixed $value - * @param callable|null $callback - * @param callable|null $default - * @return $this|mixed + * @template TWhenReturnType + * + * @param bool $value + * @param (callable($this): TWhenReturnType)|null $callback + * @param (callable($this): TWhenReturnType)|null $default + * @return $this|TWhenReturnType */ public function when($value, callable $callback = null, callable $default = null) { @@ -35,10 +37,12 @@ public function when($value, callable $callback = null, callable $default = null /** * Apply the callback if the given "value" is (or resolves to) falsy. * - * @param mixed $value - * @param callable|null $callback - * @param callable|null $default - * @return $this|mixed + * @template TUnlessReturnType + * + * @param bool $value + * @param (callable($this): TUnlessReturnType) $callback + * @param (callable($this): TUnlessReturnType)|null $default + * @return $this|TUnlessReturnType */ public function unless($value, callable $callback = null, callable $default = null) { diff --git a/src/Illuminate/Contracts/Support/Arrayable.php b/src/Illuminate/Contracts/Support/Arrayable.php index 5a007c4fa00e..3194bd1163e0 100755 --- a/src/Illuminate/Contracts/Support/Arrayable.php +++ b/src/Illuminate/Contracts/Support/Arrayable.php @@ -3,7 +3,7 @@ namespace Illuminate\Contracts\Support; /** - * @template TKey as array-key + * @template TKey of array-key * @template TValue */ interface Arrayable diff --git a/types/Support/Collection.php b/types/Support/Collection.php index e6b683cc36f7..d7a7a8039b2d 100644 --- a/types/Support/Collection.php +++ b/types/Support/Collection.php @@ -13,7 +13,7 @@ class User extends Authenticatable $arrayable = []; /** @var iterable $iterable */ $iterable = []; -/** @var Traversable $traversable */ +/** @var Traversable $traversable */ $traversable = []; assertType('Illuminate\Support\Collection', $collection); @@ -45,6 +45,10 @@ class User extends Authenticatable return new User; })); +assertType('Illuminate\Support\Collection', $collection::times(10, function () { + return new User; +})); + assertType('Illuminate\Support\Collection', $collection->each(function ($user) { assertType('User', $user); })); @@ -135,10 +139,226 @@ class User extends Authenticatable return -1; })); -assertType('array', $collection->all()); +assertType('Illuminate\Support\Collection', $collection::make([3, 4])->diffAssoc([1, 2])); +assertType('Illuminate\Support\Collection', $collection::make(['string' => 'string'])->diffAssoc(['string' => 'string'])); -assertType('User|null', $collection->get(0)); -assertType('string|User', $collection->get(0, 'string')); +assertType('Illuminate\Support\Collection', $collection::make([3, 4])->diffAssocUsing([1, 2], function ($int) { + assertType('int', $int); + + return -1; +})); +assertType('Illuminate\Support\Collection', $collection::make(['string-1'])->diffAssocUsing(['string-2'], function ($int) { + assertType('int', $int); + + return -1; +})); + +assertType('Illuminate\Support\Collection', $collection::make([3, 4])->diffKeys([1, 2])); +assertType('Illuminate\Support\Collection', $collection::make(['string' => 'string'])->diffKeys(['string' => 'string'])); + +assertType('Illuminate\Support\Collection', $collection::make([3, 4])->diffKeysUsing([1, 2], function ($int) { + assertType('int', $int); + + return -1; +})); +assertType('Illuminate\Support\Collection', $collection::make(['string-1'])->diffKeysUsing(['string-2'], function ($int) { + assertType('int', $int); + + return -1; +})); + +assertType('Illuminate\Support\Collection', $collection::make(['string' => 'string']) + ->duplicates()); +assertType('Illuminate\Support\Collection', $collection->duplicates('name', true)); +assertType('Illuminate\Support\Collection', $collection::make([3, 'string']) + ->duplicates(function ($intOrString) { + assertType('int|string', $intOrString); + + return true; + })); + +assertType('Illuminate\Support\Collection', $collection::make(['string' => 'string']) + ->duplicatesStrict()); +assertType('Illuminate\Support\Collection', $collection->duplicatesStrict('name')); +assertType('Illuminate\Support\Collection', $collection::make([3, 'string']) + ->duplicatesStrict(function ($intOrString) { + assertType('int|string', $intOrString); + + return true; + })); + +assertType('Illuminate\Support\Collection', $collection->each(function ($user) { + assertType('User', $user); + + return null; +})); +assertType('Illuminate\Support\Collection', $collection->each(function ($user) { + assertType('User', $user); +})); + +assertType('Illuminate\Support\Collection', $collection::make([['string']]) + ->eachSpread(function ($int, $string) { + // assertType('int', $int); + // assertType('int', $string); + + return null; + })); +assertType('Illuminate\Support\Collection', $collection::make([[1, 'string']]) + ->eachSpread(function ($int, $string) { + // assertType('int', $int); + // assertType('int', $string); + })); + +assertType('bool', $collection->every(function ($user, $int) { + assertType('int', $int); + assertType('User', $user); + + return true; +})); +assertType('bool', $collection::make(['string'])->every('string', '=', 'string')); + +assertType('Illuminate\Support\Collection', $collection::make(['string' => 'string'])->except(['string'])); +assertType('Illuminate\Support\Collection', $collection->except([1])); +assertType('Illuminate\Support\Collection', $collection::make(['string']) + ->except($collection->keys()->toArray())); + +assertType('Illuminate\Support\Collection', $collection->filter()); +assertType('Illuminate\Support\Collection', $collection->filter(function ($user) { + assertType('User', $user); + + return true; +})); + +assertType('Illuminate\Support\Collection', $collection->filter()); +assertType('Illuminate\Support\Collection', $collection->filter(function ($user) { + assertType('User', $user); + + return true; +})); + +assertType('bool|Illuminate\Support\Collection', $collection->when(true, function ($collection) { + assertType('Illuminate\Support\Collection', $collection); + + return true; +})); +assertType('Illuminate\Support\Collection|void', $collection->when(true, function ($collection) { + assertType('Illuminate\Support\Collection', $collection); +})); +assertType('Illuminate\Support\Collection|string', $collection->when(true, function ($collection) { + assertType('Illuminate\Support\Collection', $collection); + + return 'string'; +})); + +assertType('bool|Illuminate\Support\Collection', $collection->whenEmpty(function ($collection) { + assertType('Illuminate\Support\Collection', $collection); + + return true; +})); +assertType('Illuminate\Support\Collection|void', $collection->whenEmpty(function ($collection) { + assertType('Illuminate\Support\Collection', $collection); +})); +assertType('Illuminate\Support\Collection|string', $collection->whenEmpty(function ($collection) { + assertType('Illuminate\Support\Collection', $collection); + + return 'string'; +})); + +assertType('bool|Illuminate\Support\Collection', $collection->whenNotEmpty(function ($collection) { + assertType('Illuminate\Support\Collection', $collection); + + return true; +})); +assertType('Illuminate\Support\Collection|void', $collection->whenNotEmpty(function ($collection) { + assertType('Illuminate\Support\Collection', $collection); +})); +assertType('Illuminate\Support\Collection|string', $collection->whenNotEmpty(function ($collection) { + assertType('Illuminate\Support\Collection', $collection); + + return 'string'; +})); + +assertType('bool|Illuminate\Support\Collection', $collection->unless(true, function ($collection) { + assertType('Illuminate\Support\Collection', $collection); + + return true; +})); +assertType('Illuminate\Support\Collection|void', $collection->unless(true, function ($collection) { + assertType('Illuminate\Support\Collection', $collection); +})); +assertType('Illuminate\Support\Collection|string', $collection->unless(true, function ($collection) { + assertType('Illuminate\Support\Collection', $collection); + + return 'string'; +})); + +assertType('bool|Illuminate\Support\Collection', $collection->unlessEmpty(function ($collection) { + assertType('Illuminate\Support\Collection', $collection); + + return true; +})); +assertType('Illuminate\Support\Collection|void', $collection->unlessEmpty(function ($collection) { + assertType('Illuminate\Support\Collection', $collection); +})); +assertType('Illuminate\Support\Collection|string', $collection->unlessEmpty(function ($collection) { + assertType('Illuminate\Support\Collection', $collection); + + return 'string'; +})); + +assertType('bool|Illuminate\Support\Collection', $collection->unlessNotEmpty(function ($collection) { + assertType('Illuminate\Support\Collection', $collection); + + return true; +})); +assertType('Illuminate\Support\Collection|void', $collection->unlessNotEmpty(function ($collection) { + assertType('Illuminate\Support\Collection', $collection); +})); +assertType('Illuminate\Support\Collection|string', $collection->unlessNotEmpty(function ($collection) { + assertType('Illuminate\Support\Collection', $collection); + + return 'string'; +})); + +assertType("Illuminate\Support\Collection string)>", $collection::make([['string' => 'string']]) + ->where('string')); +assertType("Illuminate\Support\Collection string)>", $collection::make([['string' => 'string']]) + ->where('string', '=', 'string')); +assertType("Illuminate\Support\Collection string)>", $collection::make([['string' => 'string']]) + ->where('string', 'string')); + +assertType('Illuminate\Support\Collection', $collection->whereNull()); +assertType('Illuminate\Support\Collection', $collection->whereNull('foo')); + +assertType('Illuminate\Support\Collection', $collection->whereNotNull()); +assertType('Illuminate\Support\Collection', $collection->whereNotNull('foo')); + +assertType("Illuminate\Support\Collection int)>", $collection::make([['string' => 2]]) + ->whereStrict('string', 2)); + +assertType("Illuminate\Support\Collection int)>", $collection::make([['string' => 2]]) + ->whereIn('string', [2])); + +assertType("Illuminate\Support\Collection int)>", $collection::make([['string' => 2]]) + ->whereInStrict('string', [2])); + +assertType("Illuminate\Support\Collection int)>", $collection::make([['string' => 2]]) + ->whereBetween('string', [1, 3])); + +assertType("Illuminate\Support\Collection int)>", $collection::make([['string' => 2]]) + ->whereNotBetween('string', [1, 3])); + +assertType("Illuminate\Support\Collection int)>", $collection::make([['string' => 2]]) + ->whereNotIn('string', [2])); + +assertType("Illuminate\Support\Collection int)>", $collection::make([['string' => 2]]) + ->whereNotInStrict('string', [2])); + +assertType('Illuminate\Support\Collection', $collection::make([new User, 1]) + ->whereInstanceOf(User::class)); + +assertType('Illuminate\Support\Collection', $collection::make([new User, 1]) + ->whereInstanceOf([User::class, User::class])); assertType('User|null', $collection->first()); assertType('User|null', $collection->first(function ($user) { @@ -152,6 +372,73 @@ class User extends Authenticatable return false; }, 'string')); +assertType('User|null', $collection->firstWhere('string', 'string')); +assertType('User|null', $collection->firstWhere('string', 'string', 'string')); + +assertType('Illuminate\Support\Collection', $collection::make(['string'])->flip()); + +assertType('Illuminate\Support\Collection<(int|string), array>', $collection->groupBy('name')); +assertType('Illuminate\Support\Collection<(int|string), array>', $collection->groupBy('name', true)); +assertType('Illuminate\Support\Collection<(int|string), array>', $collection->groupBy(function ($user, $int) { + // assertType('User', $user); + // assertType('int', $int); + + return 'foo'; +})); +assertType('Illuminate\Support\Collection<(int|string), array>', $collection->groupBy(function ($user) { + return 'foo'; +})); + +assertType('Illuminate\Support\Collection<(int|string), array>', $collection->keyBy('name')); +assertType('Illuminate\Support\Collection<(int|string), array>', $collection->keyBy(function ($user, $int) { + // assertType('User', $user); + // assertType('int', $int); + + return 'foo'; +})); + +assertType('bool', $collection->has(0)); +assertType('bool', $collection->has([0, 1])); + +assertType('Illuminate\Support\Collection', $collection->intersect([new User])); + +assertType('Illuminate\Support\Collection', $collection->intersectByKeys([new User])); + +assertType('Illuminate\Support\Collection', $collection->keys()); + +assertType('User|null', $collection->last()); +assertType('User|null', $collection->last(function ($user, $int) { + assertType('User', $user); + assertType('int', $int); + + return true; +})); +assertType('string|User', $collection->last(function () { + return true; +}, 'string')); + +assertType('Illuminate\Support\Collection', $collection->map(function () { + return 1; +})); +assertType('Illuminate\Support\Collection', $collection->map(function () { + return 'string'; +})); + +assertType('Illuminate\Support\Collection', $collection::make(['string']) + ->map(function ($string, $int) { + assertType('string', $string); + assertType('int', $int); + + return (string) $string; + })); + +assertType('Illuminate\Support\Collection', $collection->make([1])->push(2)); + +assertType('array', $collection->all()); + +assertType('User|null', $collection->get(0)); +assertType('string|User', $collection->get(0, 'string')); + assertType('array', $collection->toArray()); assertType('array', collect(['string' => 'string'])->toArray()); assertType('array', collect([1, 2])->toArray()); From 09b77c3251b9f6aae8c6c4d54736f269a4624499 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Fri, 13 Aug 2021 02:12:34 +0100 Subject: [PATCH 09/23] Adds work in progress regarding generic collections --- src/Illuminate/Collections/Collection.php | 48 ++++--- src/Illuminate/Collections/Enumerable.php | 100 +++++++++------ .../Collections/Traits/EnumeratesValues.php | 46 ++++--- types/Support/Collection.php | 121 ++++++++++++++++++ 4 files changed, 234 insertions(+), 81 deletions(-) diff --git a/src/Illuminate/Collections/Collection.php b/src/Illuminate/Collections/Collection.php index 5f51041d51a8..b3f8d9f0ba38 100644 --- a/src/Illuminate/Collections/Collection.php +++ b/src/Illuminate/Collections/Collection.php @@ -667,8 +667,11 @@ public function map(callable $callback) * * The callback should return an associative array with a single key/value pair. * - * @param callable $callback - * @return static + * @template TMapToDictionaryKey of array-key + * @template TMapToDictionaryValue + * + * @param callable(TValue, TKey): array $callback + * @return static> */ public function mapToDictionary(callable $callback) { @@ -696,8 +699,11 @@ public function mapToDictionary(callable $callback) * * The callback should return an associative array with a single key/value pair. * - * @param callable $callback - * @return static + * @template TMapWithKeysKey of array-key + * @template TMapWithKeysValue + * + * @param callable(TValue, TKey): array $callback + * @return static */ public function mapWithKeys(callable $callback) { @@ -717,8 +723,8 @@ public function mapWithKeys(callable $callback) /** * Merge the collection with the given items. * - * @param mixed $items - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static */ public function merge($items) { @@ -728,8 +734,8 @@ public function merge($items) /** * Recursively merge the collection with the given items. * - * @param mixed $items - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static> */ public function mergeRecursive($items) { @@ -739,8 +745,10 @@ public function mergeRecursive($items) /** * Create a collection by using this collection for keys and another for its values. * - * @param mixed $values - * @return static + * @template TCombineValue + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values + * @return static */ public function combine($values) { @@ -750,8 +758,8 @@ public function combine($values) /** * Union the collection with the given items. * - * @param mixed $items - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static */ public function union($items) { @@ -763,7 +771,7 @@ public function union($items) * * @param int $step * @param int $offset - * @return static + * @return static */ public function nth($step, $offset = 0) { @@ -785,8 +793,8 @@ public function nth($step, $offset = 0) /** * Get the items with the specified keys. * - * @param mixed $keys - * @return static + * @param \Illuminate\Support\Enumerable|array $keys + * @return static */ public function only($keys) { @@ -862,8 +870,8 @@ public function push(...$values) /** * Push all of the given items onto the collection. * - * @param iterable $source - * @return static + * @param iterable $source + * @return static */ public function concat($source) { @@ -906,7 +914,7 @@ public function put($key, $value) * Get one or a specified number of items randomly from the collection. * * @param int|null $number - * @return static|mixed + * @return static|TValue * * @throws \InvalidArgumentException */ @@ -922,8 +930,8 @@ public function random($number = null) /** * Replace the collection items with the given items. * - * @param mixed $items - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static */ public function replace($items) { diff --git a/src/Illuminate/Collections/Enumerable.php b/src/Illuminate/Collections/Enumerable.php index 438452b6bc06..57f2ce7c68b9 100644 --- a/src/Illuminate/Collections/Enumerable.php +++ b/src/Illuminate/Collections/Enumerable.php @@ -62,7 +62,7 @@ public static function wrap($value); * @template TUnwrapKey of array-key * @template TUnwrapValue * - * @param array|static $value + * @param array|static $value * @return array */ public static function unwrap($value); @@ -280,7 +280,7 @@ public function except($keys); /** * Run a filter over each of the items. * - * @param (callable(TValue): bool)|null $callback + * @param (callable(TValue): bool)|null $callback * @return static */ public function filter(callable $callback = null); @@ -603,8 +603,10 @@ public function map(callable $callback); /** * Run a map over each nested chunk of items. * - * @param callable $callback - * @return static + * @template TMapSpreadValue + * + * @param callable(...mixed): TMapSpreadValue $callback + * @return static */ public function mapSpread(callable $callback); @@ -613,8 +615,11 @@ public function mapSpread(callable $callback); * * The callback should return an associative array with a single key/value pair. * - * @param callable $callback - * @return static + * @template TMapToDictionaryKey of array-key + * @template TMapToDictionaryValue + * + * @param callable(TValue, TKey): array $callback + * @return static> */ public function mapToDictionary(callable $callback); @@ -623,8 +628,11 @@ public function mapToDictionary(callable $callback); * * The callback should return an associative array with a single key/value pair. * - * @param callable $callback - * @return static + * @template TMapToGroupsKey of array-key + * @template TMapToGroupsValue + * + * @param callable(TValue, TKey): array $callback + * @return static> */ public function mapToGroups(callable $callback); @@ -633,72 +641,77 @@ public function mapToGroups(callable $callback); * * The callback should return an associative array with a single key/value pair. * - * @param callable $callback - * @return static + * @template TMapWithKeysKey of array-key + * @template TMapWithKeysValue + * + * @param callable(TValue, TKey): array $callback + * @return static */ public function mapWithKeys(callable $callback); /** * Map a collection and flatten the result by a single level. * - * @param callable $callback - * @return static + * @param callable(TValue, TKey): mixed $callback + * @return static */ public function flatMap(callable $callback); /** * Map the values into a new class. * - * @param string $class - * @return static + * @param class-string $class + * @return static */ public function mapInto($class); /** * Merge the collection with the given items. * - * @param mixed $items - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static */ public function merge($items); /** * Recursively merge the collection with the given items. * - * @param mixed $items - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static> */ public function mergeRecursive($items); /** * Create a collection by using this collection for keys and another for its values. * - * @param mixed $values - * @return static + * @template TCombineValue + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values + * @return static */ public function combine($values); /** * Union the collection with the given items. * - * @param mixed $items - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static */ public function union($items); /** * Get the min value of a given key. * - * @param callable|string|null $callback - * @return mixed + * @param (callable(TValue):mixed)|string|null $callback + * @return TValue */ public function min($callback = null); /** * Get the max value of a given key. * - * @param callable|string|null $callback - * @return mixed + * @param (callable(TValue):mixed)|string|null $callback + * @return TValue */ public function max($callback = null); @@ -707,15 +720,15 @@ public function max($callback = null); * * @param int $step * @param int $offset - * @return static + * @return static */ public function nth($step, $offset = 0); /** * Get the items with the specified keys. * - * @param mixed $keys - * @return static + * @param \Illuminate\Support\Enumerable|array $keys + * @return static */ public function only($keys); @@ -724,25 +737,25 @@ public function only($keys); * * @param int $page * @param int $perPage - * @return static + * @return static */ public function forPage($page, $perPage); /** * Partition the collection into two arrays using the given callback or key. * - * @param callable|string $key - * @param mixed $operator - * @param mixed $value - * @return static + * @param (callable(TValue, TKey): bool)|TValue|string $key + * @param TValue|string|null $operator + * @param TValue|null $value + * @return array> */ public function partition($key, $operator = null, $value = null); /** * Push all of the given items onto the collection. * - * @param iterable $source - * @return static + * @param iterable $source + * @return static */ public function concat($source); @@ -750,7 +763,7 @@ public function concat($source); * Get one or a specified number of items randomly from the collection. * * @param int|null $number - * @return static|mixed + * @return static|TValue * * @throws \InvalidArgumentException */ @@ -759,17 +772,20 @@ public function random($number = null); /** * Reduce the collection to a single value. * - * @param callable $callback - * @param mixed $initial - * @return mixed + * @template TReduceInitial + * @template TReduceReturnType + * + * @param callable(TReduceInitial|TReduceReturnType, TValue): TReduceReturnType $callback + * @param TReduceInitial $initial + * @return TReduceReturnType */ public function reduce(callable $callback, $initial = null); /** * Replace the collection items with the given items. * - * @param mixed $items - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static */ public function replace($items); diff --git a/src/Illuminate/Collections/Traits/EnumeratesValues.php b/src/Illuminate/Collections/Traits/EnumeratesValues.php index 45d87b6fbc6c..d78422282fb4 100644 --- a/src/Illuminate/Collections/Traits/EnumeratesValues.php +++ b/src/Illuminate/Collections/Traits/EnumeratesValues.php @@ -323,8 +323,10 @@ public function isNotEmpty() /** * Run a map over each nested chunk of items. * - * @param callable $callback - * @return static + * @template TMapSpreadValue + * + * @param callable(mixed): TMapSpreadValue $callback + * @return static */ public function mapSpread(callable $callback) { @@ -340,8 +342,11 @@ public function mapSpread(callable $callback) * * The callback should return an associative array with a single key/value pair. * - * @param callable $callback - * @return static + * @template TMapToGroupsKey of array-key + * @template TMapToGroupsValue + * + * @param callable(TValue, TKey): array $callback + * @return static> */ public function mapToGroups(callable $callback) { @@ -353,8 +358,8 @@ public function mapToGroups(callable $callback) /** * Map a collection and flatten the result by a single level. * - * @param callable $callback - * @return static + * @param callable(TValue, TKey): mixed $callback + * @return static */ public function flatMap(callable $callback) { @@ -364,8 +369,8 @@ public function flatMap(callable $callback) /** * Map the values into a new class. * - * @param string $class - * @return static + * @param class-string $class + * @return static */ public function mapInto($class) { @@ -377,8 +382,8 @@ public function mapInto($class) /** * Get the min value of a given key. * - * @param callable|string|null $callback - * @return mixed + * @param (callable(TValue):mixed)|string|null $callback + * @return TValue */ public function min($callback = null) { @@ -396,8 +401,8 @@ public function min($callback = null) /** * Get the max value of a given key. * - * @param callable|string|null $callback - * @return mixed + * @param (callable(TValue):mixed)|string|null $callback + * @return TValue */ public function max($callback = null) { @@ -429,10 +434,10 @@ public function forPage($page, $perPage) /** * Partition the collection into two arrays using the given callback or key. * - * @param callable|string $key - * @param mixed $operator - * @param mixed $value - * @return static + * @param (callable(TValue, TKey): bool)|TValue|string $key + * @param TValue|string|null $operator + * @param TValue|null $value + * @return array> */ public function partition($key, $operator = null, $value = null) { @@ -707,9 +712,12 @@ public function pipeInto($class) /** * Reduce the collection to a single value. * - * @param callable $callback - * @param mixed $initial - * @return mixed + * @template TReduceInitial + * @template TReduceReturnType + * + * @param callable(TReduceInitial|TReduceReturnType, TValue): TReduceReturnType $callback + * @param TReduceInitial $initial + * @return TReduceReturnType */ public function reduce(callable $callback, $initial = null) { diff --git a/types/Support/Collection.php b/types/Support/Collection.php index d7a7a8039b2d..ed87613e0e08 100644 --- a/types/Support/Collection.php +++ b/types/Support/Collection.php @@ -432,8 +432,129 @@ class User extends Authenticatable return (string) $string; })); +assertType('Illuminate\Support\Collection', $collection::make(['string']) + ->mapSpread(function () { + return 'string'; + })); + +assertType('Illuminate\Support\Collection', $collection::make(['string']) + ->mapSpread(function () { + return 1; + })); + +assertType('Illuminate\Support\Collection>', $collection::make(['string', 'string']) + ->mapToDictionary(function ($stringValue, $stringKey) { + assertType('string', $stringValue); + assertType('int', $stringKey); + + return ['string' => 1]; + })); + +assertType('Illuminate\Support\Collection>', $collection::make(['string', 'string']) + ->mapToGroups(function ($stringValue, $stringKey) { + assertType('string', $stringValue); + assertType('int', $stringKey); + + return ['string' => 1]; + })); + +assertType('Illuminate\Support\Collection', $collection::make(['string']) + ->mapWithKeys(function ($string, $int) { + assertType('string', $string); + assertType('int', $int); + + return ['string' => 1]; + })); + +assertType('Illuminate\Support\Collection', $collection::make(['string']) + ->flatMap(function ($string, $int) { + assertType('string', $string); + assertType('int', $int); + + return 1; + })); + +assertType('Illuminate\Support\Collection', $collection->mapInto(User::class)); + +assertType('Illuminate\Support\Collection', $collection->make([1])->merge([2])); +assertType('Illuminate\Support\Collection', $collection->make(['string'])->merge(['string'])); + +assertType('Illuminate\Support\Collection>', $collection->make([1])->mergeRecursive([2])); +assertType('Illuminate\Support\Collection>', $collection->make(['string'])->mergeRecursive(['string'])); + +assertType('Illuminate\Support\Collection', $collection->make(['string' => 'string'])->combine([2])); +assertType('Illuminate\Support\Collection', $collection->make([1])->combine([1])); + +assertType('Illuminate\Support\Collection', $collection->make([1])->union([1])); +assertType('Illuminate\Support\Collection', $collection->make(['string' => 'string'])->union(['string' => 'string'])); + +assertType('int', $collection->make([1])->min()); +assertType('int', $collection->make([1])->min('string')); +assertType('int', $collection->make([1])->min(function ($int) { + assertType('int', $int); + + return 1; +})); + +assertType('int', $collection->make([1])->max()); +assertType('int', $collection->make([1])->max('string')); +assertType('int', $collection->make([1])->max(function ($int) { + assertType('int', $int); + + return 1; +})); + +assertType('Illuminate\Support\Collection', $collection->nth(1, 2)); + +assertType('Illuminate\Support\Collection', $collection::make(['string' => 'string'])->only(['string'])); +assertType('Illuminate\Support\Collection', $collection->only([1])); +assertType('Illuminate\Support\Collection', $collection::make(['string']) + ->only($collection->keys()->toArray())); + +assertType('Illuminate\Support\Collection', $collection->forPage(1, 2)); + +assertType('array>', $collection->partition(function ($user, $int) { + assertType('User', $user); + assertType('int', $int); + + return true; +})); + +assertType('array>', $collection::make(['string'])->partition('string', '=', 'string')); +assertType('array>', $collection::make(['string'])->partition('string', 'string')); +assertType('array>', $collection::make(['string'])->partition('string', )); + +assertType('Illuminate\Support\Collection', $collection->make([1])->concat([2])); +assertType('Illuminate\Support\Collection', $collection->make(['string'])->concat(['string'])); + +assertType('Illuminate\Support\Collection|int', $collection->make([1])->random(2)); +assertType('Illuminate\Support\Collection|string', $collection->make(['string'])->random()); + +assertType('int', $collection + ->reduce(function ($null, $user) { + assertType('User', $user); + assertType('int|null', $null); + + return 1; + })); + + +assertType('int', $collection + ->reduce(function ($int, $user) { + assertType('User', $user); + assertType('int', $int); + + return 1; + }, 0)); + +assertType('Illuminate\Support\Collection', $collection::make([1])->replace([1])); +assertType('Illuminate\Support\Collection', $collection->replace([new User])); + assertType('Illuminate\Support\Collection', $collection->make([1])->push(2)); + + + assertType('array', $collection->all()); assertType('User|null', $collection->get(0)); From acf574ef24e6357957a46f07ca234bbae9089433 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Fri, 13 Aug 2021 17:13:13 +0100 Subject: [PATCH 10/23] Adds work in progress regarding generic collections --- src/Illuminate/Collections/Collection.php | 90 ++++---- src/Illuminate/Collections/Enumerable.php | 114 +++++----- .../Collections/Traits/EnumeratesValues.php | 24 ++- types/Support/Collection.php | 197 +++++++++++++++++- 4 files changed, 314 insertions(+), 111 deletions(-) diff --git a/src/Illuminate/Collections/Collection.php b/src/Illuminate/Collections/Collection.php index b3f8d9f0ba38..3379ea2af4ca 100644 --- a/src/Illuminate/Collections/Collection.php +++ b/src/Illuminate/Collections/Collection.php @@ -636,9 +636,9 @@ public function last(callable $callback = null, $default = null) /** * Get the values of a given key. * - * @param string|array|int|null $value + * @param string|array $value * @param string|null $key - * @return static + * @return static */ public function pluck($value, $key = null) { @@ -941,8 +941,8 @@ public function replace($items) /** * Recursively replace the collection items with the given items. * - * @param mixed $items - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static */ public function replaceRecursive($items) { @@ -952,7 +952,7 @@ public function replaceRecursive($items) /** * Reverse items order. * - * @return static + * @return static */ public function reverse() { @@ -962,9 +962,9 @@ public function reverse() /** * Search the collection for a given value and return the corresponding key if successful. * - * @param mixed $value + * @param TValue|callable(TValue,TKey): bool $value * @param bool $strict - * @return mixed + * @return TKey|bool */ public function search($value, $strict = false) { @@ -1012,7 +1012,7 @@ public function shift($count = 1) * Shuffle the items in the collection. * * @param int|null $seed - * @return static + * @return static */ public function shuffle($seed = null) { @@ -1039,7 +1039,7 @@ public function sliding($size = 2, $step = 1) * Skip the first {$count} items. * * @param int $count - * @return static + * @return static */ public function skip($count) { @@ -1049,8 +1049,8 @@ public function skip($count) /** * Skip items in the collection until the given condition is met. * - * @param mixed $value - * @return static + * @param TValue|callable(TValue,TKey): bool $value + * @return static */ public function skipUntil($value) { @@ -1060,8 +1060,8 @@ public function skipUntil($value) /** * Skip items in the collection while the given condition is met. * - * @param mixed $value - * @return static + * @param TValue|callable(TValue,TKey): bool $value + * @return static */ public function skipWhile($value) { @@ -1073,7 +1073,7 @@ public function skipWhile($value) * * @param int $offset * @param int|null $length - * @return static + * @return static */ public function slice($offset, $length = null) { @@ -1084,7 +1084,7 @@ public function slice($offset, $length = null) * Split a collection into a certain number of groups. * * @param int $numberOfGroups - * @return static + * @return static> */ public function split($numberOfGroups) { @@ -1131,10 +1131,10 @@ public function splitIn($numberOfGroups) /** * Get the first item in the collection, but only if exactly one item exists. Otherwise, throw an exception. * - * @param mixed $key - * @param mixed $operator - * @param mixed $value - * @return mixed + * @param (callable(TValue, TKey): bool)|string $key + * @param TValue|string|null $operator + * @param TValue|null $value + * @return TValue * * @throws \Illuminate\Support\ItemNotFoundException * @throws \Illuminate\Support\MultipleItemsFoundException @@ -1187,7 +1187,7 @@ public function firstOrFail($key = null, $operator = null, $value = null) * Chunk the collection into chunks of the given size. * * @param int $size - * @return static + * @return static> */ public function chunk($size) { @@ -1207,8 +1207,8 @@ public function chunk($size) /** * Chunk the collection into chunks with a callback. * - * @param callable $callback - * @return static + * @param callable(TValue, TKey, static): bool $callback + * @return static> */ public function chunkWhile(callable $callback) { @@ -1220,8 +1220,8 @@ public function chunkWhile(callable $callback) /** * Sort through each item with a callback. * - * @param callable|int|null $callback - * @return static + * @param (callable(TValue, TValue): bool)|null|int $callback + * @return static */ public function sort($callback = null) { @@ -1238,7 +1238,7 @@ public function sort($callback = null) * Sort items in descending order. * * @param int $options - * @return static + * @return static */ public function sortDesc($options = SORT_REGULAR) { @@ -1252,10 +1252,10 @@ public function sortDesc($options = SORT_REGULAR) /** * Sort the collection using the given callback. * - * @param callable|array|string $callback + * @param (callable(TValue, TKey): mixed)|string $callback * @param int $options * @param bool $descending - * @return static + * @return static */ public function sortBy($callback, $options = SORT_REGULAR, $descending = false) { @@ -1334,9 +1334,9 @@ protected function sortByMany(array $comparisons = []) /** * Sort the collection in descending order using the given callback. * - * @param callable|string $callback + * @param (callable(TValue, TKey): mixed)|string $callback * @param int $options - * @return static + * @return static */ public function sortByDesc($callback, $options = SORT_REGULAR) { @@ -1348,7 +1348,7 @@ public function sortByDesc($callback, $options = SORT_REGULAR) * * @param int $options * @param bool $descending - * @return static + * @return static */ public function sortKeys($options = SORT_REGULAR, $descending = false) { @@ -1363,7 +1363,7 @@ public function sortKeys($options = SORT_REGULAR, $descending = false) * Sort the collection keys in descending order. * * @param int $options - * @return static + * @return static */ public function sortKeysDesc($options = SORT_REGULAR) { @@ -1391,7 +1391,7 @@ public function splice($offset, $length = null, $replacement = []) * Take the first or last {$limit} items. * * @param int $limit - * @return static + * @return static */ public function take($limit) { @@ -1405,8 +1405,8 @@ public function take($limit) /** * Take items in the collection until the given condition is met. * - * @param mixed $value - * @return static + * @param TValue|callable(TValue,TKey): bool $value + * @return static */ public function takeUntil($value) { @@ -1416,8 +1416,8 @@ public function takeUntil($value) /** * Take items in the collection while the given condition is met. * - * @param mixed $value - * @return static + * @param TValue|callable(TValue,TKey): bool $value + * @return static */ public function takeWhile($value) { @@ -1440,7 +1440,7 @@ public function transform(callable $callback) /** * Reset the keys on the underlying array. * - * @return static + * @return static */ public function values() { @@ -1453,8 +1453,10 @@ public function values() * e.g. new Collection([1, 2, 3])->zip([4, 5, 6]); * => [[1, 4], [2, 5], [3, 6]] * - * @param mixed ...$items - * @return static + * @template TZipValue + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable ...$items + * @return static> */ public function zip($items) { @@ -1472,9 +1474,11 @@ public function zip($items) /** * Pad collection to the specified length with a value. * + * @template TPadValue + * * @param int $size - * @param mixed $value - * @return static + * @param TPadValue $value + * @return static */ public function pad($size, $value) { @@ -1506,8 +1510,8 @@ public function count() /** * Count the number of items in the collection by a field or using a callback. * - * @param callable|string $countBy - * @return static + * @param (callable(TValue, TKey): mixed)|string|null $countBy + * @return static */ public function countBy($countBy = null) { diff --git a/src/Illuminate/Collections/Enumerable.php b/src/Illuminate/Collections/Enumerable.php index 57f2ce7c68b9..194653567ef4 100644 --- a/src/Illuminate/Collections/Enumerable.php +++ b/src/Illuminate/Collections/Enumerable.php @@ -792,24 +792,24 @@ public function replace($items); /** * Recursively replace the collection items with the given items. * - * @param mixed $items - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static */ public function replaceRecursive($items); /** * Reverse items order. * - * @return static + * @return static */ public function reverse(); /** * Search the collection for a given value and return the corresponding key if successful. * - * @param mixed $value + * @param TValue|callable(TValue,TKey): bool $value * @param bool $strict - * @return mixed + * @return TKey|bool */ public function search($value, $strict = false); @@ -817,7 +817,7 @@ public function search($value, $strict = false); * Shuffle the items in the collection. * * @param int|null $seed - * @return static + * @return static */ public function shuffle($seed = null); @@ -825,23 +825,23 @@ public function shuffle($seed = null); * Skip the first {$count} items. * * @param int $count - * @return static + * @return static */ public function skip($count); /** * Skip items in the collection until the given condition is met. * - * @param mixed $value - * @return static + * @param TValue|callable(TValue,TKey): bool $value + * @return static */ public function skipUntil($value); /** * Skip items in the collection while the given condition is met. * - * @param mixed $value - * @return static + * @param TValue|callable(TValue,TKey): bool $value + * @return static */ public function skipWhile($value); @@ -850,7 +850,7 @@ public function skipWhile($value); * * @param int $offset * @param int|null $length - * @return static + * @return static */ public function slice($offset, $length = null); @@ -858,17 +858,17 @@ public function slice($offset, $length = null); * Split a collection into a certain number of groups. * * @param int $numberOfGroups - * @return static + * @return static> */ public function split($numberOfGroups); /** * Get the first item in the collection, but only if exactly one item exists. Otherwise, throw an exception. * - * @param mixed $key - * @param mixed $operator - * @param mixed $value - * @return mixed + * @param (callable(TValue, TKey): bool)|string $key + * @param TValue|string|null $operator + * @param TValue|null $value + * @return TValue * * @throws \Illuminate\Support\ItemNotFoundException * @throws \Illuminate\Support\MultipleItemsFoundException @@ -879,23 +879,23 @@ public function sole($key = null, $operator = null, $value = null); * Chunk the collection into chunks of the given size. * * @param int $size - * @return static + * @return static> */ public function chunk($size); /** * Chunk the collection into chunks with a callback. * - * @param callable $callback - * @return static + * @param callable(TValue, TKey, static): bool $callback + * @return static> */ public function chunkWhile(callable $callback); /** * Sort through each item with a callback. * - * @param callable|null|int $callback - * @return static + * @param (callable(TValue, TValue): bool)|null|int $callback + * @return static */ public function sort($callback = null); @@ -903,26 +903,26 @@ public function sort($callback = null); * Sort items in descending order. * * @param int $options - * @return static + * @return static */ public function sortDesc($options = SORT_REGULAR); /** * Sort the collection using the given callback. * - * @param callable|string $callback + * @param (callable(TValue, TKey): mixed)|string $callback * @param int $options * @param bool $descending - * @return static + * @return static */ public function sortBy($callback, $options = SORT_REGULAR, $descending = false); /** * Sort the collection in descending order using the given callback. * - * @param callable|string $callback + * @param (callable(TValue, TKey): mixed)|string $callback * @param int $options - * @return static + * @return static */ public function sortByDesc($callback, $options = SORT_REGULAR); @@ -931,7 +931,7 @@ public function sortByDesc($callback, $options = SORT_REGULAR); * * @param int $options * @param bool $descending - * @return static + * @return static */ public function sortKeys($options = SORT_REGULAR, $descending = false); @@ -939,14 +939,14 @@ public function sortKeys($options = SORT_REGULAR, $descending = false); * Sort the collection keys in descending order. * * @param int $options - * @return static + * @return static */ public function sortKeysDesc($options = SORT_REGULAR); /** * Get the sum of the given values. * - * @param callable|string|null $callback + * @param (callable(TValue): mixed)|string|null $callback * @return mixed */ public function sum($callback = null); @@ -955,30 +955,30 @@ public function sum($callback = null); * Take the first or last {$limit} items. * * @param int $limit - * @return static + * @return static */ public function take($limit); /** * Take items in the collection until the given condition is met. * - * @param mixed $value - * @return static + * @param TValue|callable(TValue,TKey): bool $value + * @return static */ public function takeUntil($value); /** * Take items in the collection while the given condition is met. * - * @param mixed $value - * @return static + * @param TValue|callable(TValue,TKey): bool $value + * @return static */ public function takeWhile($value); /** * Pass the collection to the given callback and then return it. * - * @param callable $callback + * @param callable(TValue): mixed $callback * @return $this */ public function tap(callable $callback); @@ -986,66 +986,70 @@ public function tap(callable $callback); /** * Pass the enumerable to the given callback and return the result. * - * @param callable $callback - * @return mixed + * @template TPipeReturnType + * + * @param callable($this): TPipeReturnType $callback + * @return TPipeReturnType */ public function pipe(callable $callback); /** * Get the values of a given key. * - * @param string|array $value + * @param string|array $value * @param string|null $key - * @return static + * @return static */ public function pluck($value, $key = null); /** * Create a collection of all elements that do not pass a given truth test. * - * @param callable|mixed $callback - * @return static + * @param (callable(TValue): bool)|bool $callback + * @return static */ public function reject($callback = true); /** * Return only unique items from the collection array. * - * @param string|callable|null $key + * @param (callable(TValue, TKey): bool)|string|null $key * @param bool $strict - * @return static + * @return static */ public function unique($key = null, $strict = false); /** * Return only unique items from the collection array using strict comparison. * - * @param string|callable|null $key - * @return static + * @param (callable(TValue, TKey): bool)|string|null $key + * @return static */ public function uniqueStrict($key = null); /** * Reset the keys on the underlying array. * - * @return static + * @return static */ public function values(); /** * Pad collection to the specified length with a value. * + * @template TPadValue + * * @param int $size - * @param mixed $value - * @return static + * @param TPadValue $value + * @return static */ public function pad($size, $value); /** * Count the number of items in the collection using a given truth test. * - * @param callable|null $callback - * @return static + * @param (callable(TValue, TKey): mixed)|string|null $callback + * @return static */ public function countBy($callback = null); @@ -1055,15 +1059,17 @@ public function countBy($callback = null); * e.g. new Collection([1, 2, 3])->zip([4, 5, 6]); * => [[1, 4], [2, 5], [3, 6]] * - * @param mixed ...$items - * @return static + * @template TZipValue + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable ...$items + * @return static> */ public function zip($items); /** * Collect the values into a collection. * - * @return \Illuminate\Support\Collection + * @return \Illuminate\Support\Collection */ public function collect(); diff --git a/src/Illuminate/Collections/Traits/EnumeratesValues.php b/src/Illuminate/Collections/Traits/EnumeratesValues.php index d78422282fb4..d92d0a8d32af 100644 --- a/src/Illuminate/Collections/Traits/EnumeratesValues.php +++ b/src/Illuminate/Collections/Traits/EnumeratesValues.php @@ -462,7 +462,7 @@ public function partition($key, $operator = null, $value = null) /** * Get the sum of the given values. * - * @param callable|string|null $callback + * @param (callable(TValue): mixed)|string|null $callback * @return mixed */ public function sum($callback = null) @@ -690,8 +690,10 @@ public function whereInstanceOf($type) /** * Pass the collection to the given callback and return the result. * - * @param callable $callback - * @return mixed + * @template TPipeReturnType + * + * @param callable($this): TPipeReturnType $callback + * @return TPipeReturnType */ public function pipe(callable $callback) { @@ -745,8 +747,8 @@ public function reduceWithKeys(callable $callback, $initial = null) /** * Create a collection of all elements that do not pass a given truth test. * - * @param callable|mixed $callback - * @return static + * @param (callable(TValue): bool)|bool $callback + * @return static */ public function reject($callback = true) { @@ -762,7 +764,7 @@ public function reject($callback = true) /** * Pass the collection to the given callback and then return it. * - * @param callable $callback + * @param callable(TValue): mixed $callback * @return $this */ public function tap(callable $callback) @@ -775,9 +777,9 @@ public function tap(callable $callback) /** * Return only unique items from the collection array. * - * @param string|callable|null $key + * @param (callable(TValue, TKey): bool)|string|null $key * @param bool $strict - * @return static + * @return static */ public function unique($key = null, $strict = false) { @@ -797,8 +799,8 @@ public function unique($key = null, $strict = false) /** * Return only unique items from the collection array using strict comparison. * - * @param string|callable|null $key - * @return static + * @param (callable(TValue, TKey): bool)|string|null $key + * @return static */ public function uniqueStrict($key = null) { @@ -808,7 +810,7 @@ public function uniqueStrict($key = null) /** * Collect the values into a collection. * - * @return \Illuminate\Support\Collection + * @return \Illuminate\Support\Collection */ public function collect() { diff --git a/types/Support/Collection.php b/types/Support/Collection.php index ed87613e0e08..cc4d9273015a 100644 --- a/types/Support/Collection.php +++ b/types/Support/Collection.php @@ -519,7 +519,6 @@ class User extends Authenticatable return true; })); - assertType('array>', $collection::make(['string'])->partition('string', '=', 'string')); assertType('array>', $collection::make(['string'])->partition('string', 'string')); assertType('array>', $collection::make(['string'])->partition('string', )); @@ -538,7 +537,6 @@ class User extends Authenticatable return 1; })); - assertType('int', $collection ->reduce(function ($int, $user) { assertType('User', $user); @@ -550,10 +548,203 @@ class User extends Authenticatable assertType('Illuminate\Support\Collection', $collection::make([1])->replace([1])); assertType('Illuminate\Support\Collection', $collection->replace([new User])); -assertType('Illuminate\Support\Collection', $collection->make([1])->push(2)); +assertType('Illuminate\Support\Collection', $collection::make([1])->replaceRecursive([1])); +assertType('Illuminate\Support\Collection', $collection->replaceRecursive([new User])); + +assertType('Illuminate\Support\Collection', $collection->reverse()); + +assertType('int|bool', $collection->make([1])->search(2)); +assertType('string|bool', $collection->make(['string' => 'string'])->search('string')); +assertType('int|bool', $collection->search(function ($user, $int) { + assertType('User', $user); + assertType('int', $int); + + return true; +})); + +assertType('Illuminate\Support\Collection', $collection->make([1])->shuffle()); +assertType('Illuminate\Support\Collection', $collection->shuffle()); + +assertType('Illuminate\Support\Collection', $collection->make([1])->skip(1)); +assertType('Illuminate\Support\Collection', $collection->skip(1)); + +assertType('Illuminate\Support\Collection', $collection->make([1])->skipUntil(1)); +assertType('Illuminate\Support\Collection', $collection->skipUntil(new User)); +assertType('Illuminate\Support\Collection', $collection->skipUntil(function ($user, $int) { + assertType('User', $user); + assertType('int', $int); + + return true; +})); + +assertType('Illuminate\Support\Collection', $collection->make([1])->skipWhile(1)); +assertType('Illuminate\Support\Collection', $collection->skipWhile(new User)); +assertType('Illuminate\Support\Collection', $collection->skipWhile(function ($user, $int) { + assertType('User', $user); + assertType('int', $int); + + return true; +})); + +assertType('Illuminate\Support\Collection', $collection->make([1])->slice(1)); +assertType('Illuminate\Support\Collection', $collection->slice(1, 2)); + +assertType('Illuminate\Support\Collection>', $collection->split(3)); +assertType('Illuminate\Support\Collection>', $collection->make([1])->split(3)); + +assertType('string', $collection->make(['string' => 'string'])->sole('string', 'string')); +assertType('string', $collection->make(['string' => 'string'])->sole('string', '=', 'string')); +assertType('User', $collection->sole(function ($user, $int) { + assertType('User', $user); + assertType('int', $int); + + return true; +})); + +assertType('Illuminate\Support\Collection>', $collection::make(['string'])->chunk(1)); +assertType('Illuminate\Support\Collection>', $collection->chunk(2)); + +assertType('Illuminate\Support\Collection>', $collection->chunkWhile(function ($user, $int, $collection) { + assertType('User', $user); + assertType('int', $int); + assertType('Illuminate\Support\Collection', $collection); + + return true; +})); + +assertType('Illuminate\Support\Collection', $collection->sort(function ($userA, $userB) { + assertType('User', $userA); + assertType('User', $userB); + + return true; +})); +assertType('Illuminate\Support\Collection', $collection->sort()); + +assertType('Illuminate\Support\Collection', $collection->sortDesc()); +assertType('Illuminate\Support\Collection', $collection->sortDesc(2)); + +assertType('Illuminate\Support\Collection', $collection->sortBy(function ($user, $int) { + assertType('User', $user); + assertType('int', $int); + + return 1; +})); +assertType('Illuminate\Support\Collection', $collection->sortBy('string')); +assertType('Illuminate\Support\Collection', $collection->sortBy('string', 1, false)); + +assertType('Illuminate\Support\Collection', $collection->sortByDesc(function ($user, $int) { + assertType('User', $user); + assertType('int', $int); + + return 1; +})); + +assertType('Illuminate\Support\Collection', $collection->make([1])->sortKeys()); +assertType('Illuminate\Support\Collection', $collection->make(['string' => 'string'])->sortKeys(1, true)); +assertType('Illuminate\Support\Collection', $collection->make([1])->sortKeysDesc()); +assertType('Illuminate\Support\Collection', $collection->make(['string' => 'string'])->sortKeysDesc(1)); +assertType('mixed', $collection->make([1])->sum('string')); +assertType('mixed', $collection->make(['string'])->sum(function ($string) { + assertType('string', $string); + + return 1; +})); + +assertType('Illuminate\Support\Collection', $collection->make([1])->take(1)); +assertType('Illuminate\Support\Collection', $collection->take(1)); + +assertType('Illuminate\Support\Collection', $collection->make([1])->takeUntil(1)); +assertType('Illuminate\Support\Collection', $collection->takeUntil(new User)); +assertType('Illuminate\Support\Collection', $collection->takeUntil(function ($user, $int) { + assertType('User', $user); + assertType('int', $int); + + return true; +})); + +assertType('Illuminate\Support\Collection', $collection->make([1])->takeWhile(1)); +assertType('Illuminate\Support\Collection', $collection->takeWhile(new User)); +assertType('Illuminate\Support\Collection', $collection->takeWhile(function ($user, $int) { + assertType('User', $user); + assertType('int', $int); + + return true; +})); + +assertType('Illuminate\Support\Collection', $collection->tap(function ($user) { + assertType('User', $user); +})); + +assertType('Illuminate\Support\Collection', $collection->pipe(function ($collection) { + assertType('Illuminate\Support\Collection', $collection); + + return collect([1]); +})); +assertType('int', $collection->make([1])->pipe(function ($collection) { + assertType('Illuminate\Support\Collection', $collection); + + return 1; +})); + +assertType('Illuminate\Support\Collection', $collection->make(['string' => 'string'])->pluck('string')); +assertType('Illuminate\Support\Collection', $collection->make(['string' => 'string'])->pluck('string', 'string')); + +assertType('Illuminate\Support\Collection', $collection->reject()); +assertType('Illuminate\Support\Collection', $collection->reject(function ($user) { + assertType('User', $user); + + return true; +})); + +assertType('Illuminate\Support\Collection', $collection->unique()); +assertType('Illuminate\Support\Collection', $collection->unique(function ($user, $int) { + assertType('User', $user); + assertType('int', $int); + + return true; +})); +assertType('Illuminate\Support\Collection', $collection->make(['string' => 'string'])->unique(function ($stringA, $stringB) { + assertType('string', $stringA); + assertType('string', $stringA); + + return false; +}, true)); + +assertType('Illuminate\Support\Collection', $collection->uniqueStrict()); +assertType('Illuminate\Support\Collection', $collection->uniqueStrict(function ($user, $int) { + assertType('User', $user); + assertType('int', $int); + return true; +})); + +assertType('Illuminate\Support\Collection', $collection->values()); +assertType('Illuminate\Support\Collection', $collection::make(['string', 'string'])->values()); +assertType('Illuminate\Support\Collection', $collection::make(['string', 1])->values()); + +assertType('Illuminate\Support\Collection', $collection->make([1])->pad(2, 0)); +assertType('Illuminate\Support\Collection', $collection->make([1])->pad(2, 'string')); +assertType('Illuminate\Support\Collection', $collection->pad(2, 0)); + +assertType('Illuminate\Support\Collection', $collection->make([1])->countBy()); +assertType('Illuminate\Support\Collection', $collection->make(['string' => 'string'])->countBy('string')); +assertType('Illuminate\Support\Collection', $collection->make(['string'])->countBy(function ($string, $int) { + assertType('string', $string); + assertType('int', $int); + + return false; +})); + +assertType('Illuminate\Support\Collection>', $collection->zip([1])); +assertType('Illuminate\Support\Collection>', $collection->zip(['string'])); +assertType('Illuminate\Support\Collection>', $collection::make(['string' => 'string'])->zip(['string'])); + +assertType('Illuminate\Support\Collection', $collection->collect()); +assertType('Illuminate\Support\Collection', $collection->make([1])->collect()); + +assertType('Illuminate\Support\Collection', $collection->make([1])->push(2)); assertType('array', $collection->all()); From 338110d868f557656aad4f29d0111fa66b78c4b9 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Sat, 21 Aug 2021 16:23:25 +0100 Subject: [PATCH 11/23] Adds work in progress regarding generic collections --- src/Illuminate/Collections/Collection.php | 36 +++++++++++------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/Illuminate/Collections/Collection.php b/src/Illuminate/Collections/Collection.php index 3379ea2af4ca..0ddffcaf9606 100644 --- a/src/Illuminate/Collections/Collection.php +++ b/src/Illuminate/Collections/Collection.php @@ -400,7 +400,7 @@ public function flip() * @param string|array $keys * @return $this */ - public function forget($keys) + public function forget($keys) // TODO { foreach ((array) $keys as $key) { $this->offsetUnset($key); @@ -817,7 +817,7 @@ public function only($keys) * @param int $count * @return mixed */ - public function pop($count = 1) + public function pop($count = 1) // TODO { if ($count === 1) { return array_pop($this->items); @@ -845,7 +845,7 @@ public function pop($count = 1) * @param mixed $key * @return $this */ - public function prepend($value, $key = null) + public function prepend($value, $key = null) // TODO { $this->items = Arr::prepend($this->items, ...func_get_args()); @@ -858,7 +858,7 @@ public function prepend($value, $key = null) * @param ...TValue $values * @return $this */ - public function push(...$values) + public function push(...$values) // TODO { foreach ($values as $value) { $this->items[] = $value; @@ -891,7 +891,7 @@ public function concat($source) * @param mixed $default * @return mixed */ - public function pull($key, $default = null) + public function pull($key, $default = null) // TODO { return Arr::pull($this->items, $key, $default); } @@ -903,7 +903,7 @@ public function pull($key, $default = null) * @param mixed $value * @return $this */ - public function put($key, $value) + public function put($key, $value) // TODO { $this->offsetSet($key, $value); @@ -987,7 +987,7 @@ public function search($value, $strict = false) * @param int $count * @return mixed */ - public function shift($count = 1) + public function shift($count = 1) // TODO { if ($count === 1) { return array_shift($this->items); @@ -1026,7 +1026,7 @@ public function shuffle($seed = null) * @param int $step * @return static */ - public function sliding($size = 2, $step = 1) + public function sliding($size = 2, $step = 1) // TODO { $chunks = floor(($this->count() - $size) / $step) + 1; @@ -1123,7 +1123,7 @@ public function split($numberOfGroups) * @param int $numberOfGroups * @return static */ - public function splitIn($numberOfGroups) + public function splitIn($numberOfGroups) // TODO { return $this->chunk(ceil($this->count() / $numberOfGroups)); } @@ -1378,7 +1378,7 @@ public function sortKeysDesc($options = SORT_REGULAR) * @param mixed $replacement * @return static */ - public function splice($offset, $length = null, $replacement = []) + public function splice($offset, $length = null, $replacement = []) // TODO { if (func_num_args() === 1) { return new static(array_splice($this->items, $offset)); @@ -1430,7 +1430,7 @@ public function takeWhile($value) * @param callable $callback * @return $this */ - public function transform(callable $callback) + public function transform(callable $callback) // TODO { $this->items = $this->map($callback)->all(); @@ -1491,7 +1491,7 @@ public function pad($size, $value) * @return \ArrayIterator */ #[\ReturnTypeWillChange] - public function getIterator() + public function getIterator() // TODO { return new ArrayIterator($this->items); } @@ -1524,7 +1524,7 @@ public function countBy($countBy = null) * @param mixed $item * @return $this */ - public function add($item) + public function add($item) // TODO { $this->items[] = $item; @@ -1536,7 +1536,7 @@ public function add($item) * * @return \Illuminate\Support\Collection */ - public function toBase() + public function toBase() // TODO { return new self($this); } @@ -1548,7 +1548,7 @@ public function toBase() * @return bool */ #[\ReturnTypeWillChange] - public function offsetExists($key) + public function offsetExists($key) // TODO { return isset($this->items[$key]); } @@ -1560,7 +1560,7 @@ public function offsetExists($key) * @return mixed */ #[\ReturnTypeWillChange] - public function offsetGet($key) + public function offsetGet($key) // TODO { return $this->items[$key]; } @@ -1573,7 +1573,7 @@ public function offsetGet($key) * @return void */ #[\ReturnTypeWillChange] - public function offsetSet($key, $value) + public function offsetSet($key, $value) // TODO { if (is_null($key)) { $this->items[] = $value; @@ -1589,7 +1589,7 @@ public function offsetSet($key, $value) * @return void */ #[\ReturnTypeWillChange] - public function offsetUnset($key) + public function offsetUnset($key) // TODO { unset($this->items[$key]); } From 36ab22b4cf705012907a2fbc7fe55062b1202bf5 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Mon, 23 Aug 2021 11:30:35 +0100 Subject: [PATCH 12/23] Adds work in progress regarding generic collections --- src/Illuminate/Collections/Collection.php | 90 +++++++++--------- src/Illuminate/Collections/Enumerable.php | 2 +- types/Support/Collection.php | 110 +++++++++++++++++++--- 3 files changed, 144 insertions(+), 58 deletions(-) diff --git a/src/Illuminate/Collections/Collection.php b/src/Illuminate/Collections/Collection.php index 0ddffcaf9606..6a59d436d156 100644 --- a/src/Illuminate/Collections/Collection.php +++ b/src/Illuminate/Collections/Collection.php @@ -397,10 +397,10 @@ public function flip() /** * Remove an item from the collection by key. * - * @param string|array $keys + * @param TKey|array $keys * @return $this */ - public function forget($keys) // TODO + public function forget($keys) { foreach ((array) $keys as $key) { $this->offsetUnset($key); @@ -815,9 +815,9 @@ public function only($keys) * Get and remove the last N items from the collection. * * @param int $count - * @return mixed + * @return static|TValue|null */ - public function pop($count = 1) // TODO + public function pop($count = 1) { if ($count === 1) { return array_pop($this->items); @@ -841,11 +841,11 @@ public function pop($count = 1) // TODO /** * Push an item onto the beginning of the collection. * - * @param mixed $value - * @param mixed $key + * @param TValue $value + * @param TKey $key * @return $this */ - public function prepend($value, $key = null) // TODO + public function prepend($value, $key = null) { $this->items = Arr::prepend($this->items, ...func_get_args()); @@ -858,7 +858,7 @@ public function prepend($value, $key = null) // TODO * @param ...TValue $values * @return $this */ - public function push(...$values) // TODO + public function push(...$values) { foreach ($values as $value) { $this->items[] = $value; @@ -887,11 +887,13 @@ public function concat($source) /** * Get and remove an item from the collection. * - * @param mixed $key - * @param mixed $default - * @return mixed + * @template TPullDefault + * + * @param TKey $key + * @param TPullDefault $default + * @return TValue|TPullDefault */ - public function pull($key, $default = null) // TODO + public function pull($key, $default = null) { return Arr::pull($this->items, $key, $default); } @@ -899,11 +901,11 @@ public function pull($key, $default = null) // TODO /** * Put an item in the collection by key. * - * @param mixed $key - * @param mixed $value + * @param TKey $key + * @param TValue $value * @return $this */ - public function put($key, $value) // TODO + public function put($key, $value) { $this->offsetSet($key, $value); @@ -962,7 +964,7 @@ public function reverse() /** * Search the collection for a given value and return the corresponding key if successful. * - * @param TValue|callable(TValue,TKey): bool $value + * @param TValue|(callable(TValue,TKey): bool) $value * @param bool $strict * @return TKey|bool */ @@ -985,9 +987,9 @@ public function search($value, $strict = false) * Get and remove the first N items from the collection. * * @param int $count - * @return mixed + * @return static|TValue|null */ - public function shift($count = 1) // TODO + public function shift($count = 1) { if ($count === 1) { return array_shift($this->items); @@ -1024,9 +1026,9 @@ public function shuffle($seed = null) * * @param int $size * @param int $step - * @return static + * @return static> */ - public function sliding($size = 2, $step = 1) // TODO + public function sliding($size = 2, $step = 1) { $chunks = floor(($this->count() - $size) / $step) + 1; @@ -1084,7 +1086,7 @@ public function slice($offset, $length = null) * Split a collection into a certain number of groups. * * @param int $numberOfGroups - * @return static> + * @return static> */ public function split($numberOfGroups) { @@ -1121,9 +1123,9 @@ public function split($numberOfGroups) * Split a collection into a certain number of groups, and fill the first groups completely. * * @param int $numberOfGroups - * @return static + * @return static> */ - public function splitIn($numberOfGroups) // TODO + public function splitIn($numberOfGroups) { return $this->chunk(ceil($this->count() / $numberOfGroups)); } @@ -1375,10 +1377,10 @@ public function sortKeysDesc($options = SORT_REGULAR) * * @param int $offset * @param int|null $length - * @param mixed $replacement - * @return static + * @param array $replacement + * @return static */ - public function splice($offset, $length = null, $replacement = []) // TODO + public function splice($offset, $length = null, $replacement = []) { if (func_num_args() === 1) { return new static(array_splice($this->items, $offset)); @@ -1427,10 +1429,10 @@ public function takeWhile($value) /** * Transform each item in the collection using a callback. * - * @param callable $callback + * @param callable(TValue, TKey): TValue $callback * @return $this */ - public function transform(callable $callback) // TODO + public function transform(callable $callback) { $this->items = $this->map($callback)->all(); @@ -1488,10 +1490,10 @@ public function pad($size, $value) /** * Get an iterator for the items. * - * @return \ArrayIterator + * @return \ArrayIterator */ #[\ReturnTypeWillChange] - public function getIterator() // TODO + public function getIterator() { return new ArrayIterator($this->items); } @@ -1521,10 +1523,10 @@ public function countBy($countBy = null) /** * Add an item to the collection. * - * @param mixed $item + * @param TValue $item * @return $this */ - public function add($item) // TODO + public function add($item) { $this->items[] = $item; @@ -1534,9 +1536,9 @@ public function add($item) // TODO /** * Get a base Support collection instance from this collection. * - * @return \Illuminate\Support\Collection + * @return \Illuminate\Support\Collection */ - public function toBase() // TODO + public function toBase() { return new self($this); } @@ -1544,11 +1546,11 @@ public function toBase() // TODO /** * Determine if an item exists at an offset. * - * @param mixed $key + * @param TKey $key * @return bool */ #[\ReturnTypeWillChange] - public function offsetExists($key) // TODO + public function offsetExists($key) { return isset($this->items[$key]); } @@ -1556,11 +1558,11 @@ public function offsetExists($key) // TODO /** * Get an item at a given offset. * - * @param mixed $key - * @return mixed + * @param TKey $key + * @return TValue */ #[\ReturnTypeWillChange] - public function offsetGet($key) // TODO + public function offsetGet($key) { return $this->items[$key]; } @@ -1568,12 +1570,12 @@ public function offsetGet($key) // TODO /** * Set the item at a given offset. * - * @param mixed $key - * @param mixed $value + * @param TKey|null $key + * @param TValue $value * @return void */ #[\ReturnTypeWillChange] - public function offsetSet($key, $value) // TODO + public function offsetSet($key, $value) { if (is_null($key)) { $this->items[] = $value; @@ -1585,11 +1587,11 @@ public function offsetSet($key, $value) // TODO /** * Unset the item at a given offset. * - * @param string $key + * @param TKey $key * @return void */ #[\ReturnTypeWillChange] - public function offsetUnset($key) // TODO + public function offsetUnset($key) { unset($this->items[$key]); } diff --git a/src/Illuminate/Collections/Enumerable.php b/src/Illuminate/Collections/Enumerable.php index 194653567ef4..9845da8d5e15 100644 --- a/src/Illuminate/Collections/Enumerable.php +++ b/src/Illuminate/Collections/Enumerable.php @@ -858,7 +858,7 @@ public function slice($offset, $length = null); * Split a collection into a certain number of groups. * * @param int $numberOfGroups - * @return static> + * @return static> */ public function split($numberOfGroups); diff --git a/types/Support/Collection.php b/types/Support/Collection.php index cc4d9273015a..b7308f708b34 100644 --- a/types/Support/Collection.php +++ b/types/Support/Collection.php @@ -2,6 +2,7 @@ use Illuminate\Contracts\Support\Arrayable; use Illuminate\Foundation\Auth\User as Authenticatable; +use Illuminate\Support\Collection; use function PHPStan\Testing\assertType; class User extends Authenticatable @@ -18,11 +19,6 @@ class User extends Authenticatable assertType('Illuminate\Support\Collection', $collection); -foreach ($collection as $int => $user) { - assertType('int', $int); - assertType('User', $user); -} - assertType('Illuminate\Support\Collection', collect(['string'])); assertType('Illuminate\Support\Collection', collect(['string' => new User])); assertType('Illuminate\Support\Collection', collect($arrayable)); @@ -553,14 +549,14 @@ class User extends Authenticatable assertType('Illuminate\Support\Collection', $collection->reverse()); -assertType('int|bool', $collection->make([1])->search(2)); -assertType('string|bool', $collection->make(['string' => 'string'])->search('string')); -assertType('int|bool', $collection->search(function ($user, $int) { - assertType('User', $user); - assertType('int', $int); - - return true; -})); +// assertType('int|bool', $collection->make([1])->search(2)); +// assertType('string|bool', $collection->make(['string' => 'string'])->search('string')); +// assertType('int|bool', $collection->search(function ($user, $int) { +// assertType('User', $user); +// assertType('int', $int); +// +// return true; +// })); assertType('Illuminate\Support\Collection', $collection->make([1])->shuffle()); assertType('Illuminate\Support\Collection', $collection->shuffle()); @@ -751,6 +747,94 @@ class User extends Authenticatable assertType('User|null', $collection->get(0)); assertType('string|User', $collection->get(0, 'string')); +assertType('Illuminate\Support\Collection', $collection->forget(1)); +assertType('Illuminate\Support\Collection', $collection->forget([1, 2])); + +assertType('Illuminate\Support\Collection|User|null', $collection->pop(1)); +assertType('Illuminate\Support\Collection|string|null', $collection::make([ + 'string-key-1' => 'string-value-1', + 'string-key-2' => 'string-value-2', +])->pop(2)); + +assertType('Illuminate\Support\Collection', $collection->make([1])->prepend(2)); +assertType('Illuminate\Support\Collection', $collection->prepend(new User, 2)); + +assertType('Illuminate\Support\Collection', $collection->make([1])->push(2)); +assertType('Illuminate\Support\Collection', $collection->push(new User, new User)); + +assertType('User|null', $collection->pull(1)); +assertType('string|User', $collection->pull(1, 'string')); + +assertType('Illuminate\Support\Collection', $collection->put(1, new User)); +assertType('Illuminate\Support\Collection', $collection::make([ + 'string-key-1' => 'string-value-1' +])->put('string-key-2', 'string-value-2')); + +assertType('Illuminate\Support\Collection|User|null', $collection->shift(1)); +assertType('Illuminate\Support\Collection|string|null', $collection::make([ + 'string-key-1' => 'string-value-1', + 'string-key-2' => 'string-value-2', +])->shift(2)); + +assertType( + 'Illuminate\Support\Collection>', + $collection->sliding(2) +); + +assertType( + 'Illuminate\Support\Collection>', + $collection::make(['string' => 'string'])->sliding(2, 1) +); + +assertType( + 'Illuminate\Support\Collection>', + $collection->splitIn(2) +); + +assertType( + 'Illuminate\Support\Collection>', + $collection::make(['string' => 'string'])->splitIn(1) +); + +assertType('Illuminate\Support\Collection', $collection->splice(1)); +assertType('Illuminate\Support\Collection', $collection->splice(1, 1, [new User])); + +assertType('Illuminate\Support\Collection', $collection->transform(function ($user, $int) { + assertType('User', $user); + assertType('int', $int); + + return new User; +})); + +assertType('Illuminate\Support\Collection', $collection->add(new User)); + +/** + * @template TKey of array-key + * @template TValue + * + * @extends \Illuminate\Support\Collection + */ +class CustomCollection extends Collection {} + +// assertType('CustomCollection', CustomCollection::make([new User])); +assertType('Illuminate\Support\Collection', CustomCollection::make([new User])->toBase()); + +assertType('bool', $collection->offsetExists(0)); +assertType('bool', isset($collection[0])); + +$collection->offsetSet(0, new User); +$collection->offsetSet(null, new User); +assertType('User', $collection[0] = new User); + +$collection->offsetUnset(0); +unset($collection[0]); + assertType('array', $collection->toArray()); assertType('array', collect(['string' => 'string'])->toArray()); assertType('array', collect([1, 2])->toArray()); + +assertType('ArrayIterator', $collection->getIterator()); +foreach ($collection as $int => $user) { + assertType('int', $int); + assertType('User', $user); +} From a35ddcb2765ee4c0b8b32903b21a830438867314 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Mon, 23 Aug 2021 11:35:34 +0100 Subject: [PATCH 13/23] Styling --- src/Illuminate/Collections/Collection.php | 1 + src/Illuminate/Collections/Enumerable.php | 1 + src/Illuminate/Collections/Traits/EnumeratesValues.php | 5 +++-- src/Illuminate/Collections/helpers.php | 1 + 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Illuminate/Collections/Collection.php b/src/Illuminate/Collections/Collection.php index 6a59d436d156..2e177f804646 100644 --- a/src/Illuminate/Collections/Collection.php +++ b/src/Illuminate/Collections/Collection.php @@ -11,6 +11,7 @@ /** * @template TKey of array-key * @template TValue + * * @implements \ArrayAccess * @implements \Illuminate\Support\Enumerable */ diff --git a/src/Illuminate/Collections/Enumerable.php b/src/Illuminate/Collections/Enumerable.php index 9845da8d5e15..7ca78f77c9b7 100644 --- a/src/Illuminate/Collections/Enumerable.php +++ b/src/Illuminate/Collections/Enumerable.php @@ -11,6 +11,7 @@ /** * @template TKey of array-key * @template TValue + * * @extends \Illuminate\Contracts\Support\Arrayable * @extends \IteratorAggregate */ diff --git a/src/Illuminate/Collections/Traits/EnumeratesValues.php b/src/Illuminate/Collections/Traits/EnumeratesValues.php index d92d0a8d32af..e8318cfacdf0 100644 --- a/src/Illuminate/Collections/Traits/EnumeratesValues.php +++ b/src/Illuminate/Collections/Traits/EnumeratesValues.php @@ -16,6 +16,9 @@ use Traversable; /** + * @template TKey of array-key + * @template TValue + * * @property-read HigherOrderCollectionProxy $average * @property-read HigherOrderCollectionProxy $avg * @property-read HigherOrderCollectionProxy $contains @@ -43,8 +46,6 @@ * @property-read HigherOrderCollectionProxy $unless * @property-read HigherOrderCollectionProxy $until * @property-read HigherOrderCollectionProxy $when - * @template TKey of array-key - * @template TValue */ trait EnumeratesValues { diff --git a/src/Illuminate/Collections/helpers.php b/src/Illuminate/Collections/helpers.php index 61d3dd5fa91f..44743ad73586 100644 --- a/src/Illuminate/Collections/helpers.php +++ b/src/Illuminate/Collections/helpers.php @@ -9,6 +9,7 @@ * * @template TKey of array-key * @template TValue + * * @param \Illuminate\Contracts\Support\Arrayable|iterable|null $value * @return \Illuminate\Support\Collection */ From e249f050b3199364ae2854ddc5ad0f9ddc98cda1 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 23 Aug 2021 10:30:56 +0000 Subject: [PATCH 14/23] Apply fixes from StyleCI --- types/Support/Collection.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/types/Support/Collection.php b/types/Support/Collection.php index b7308f708b34..2e36227252f2 100644 --- a/types/Support/Collection.php +++ b/types/Support/Collection.php @@ -767,7 +767,7 @@ class User extends Authenticatable assertType('Illuminate\Support\Collection', $collection->put(1, new User)); assertType('Illuminate\Support\Collection', $collection::make([ - 'string-key-1' => 'string-value-1' + 'string-key-1' => 'string-value-1', ])->put('string-key-2', 'string-value-2')); assertType('Illuminate\Support\Collection|User|null', $collection->shift(1)); @@ -814,7 +814,9 @@ class User extends Authenticatable * * @extends \Illuminate\Support\Collection */ -class CustomCollection extends Collection {} +class CustomCollection extends Collection +{ +} // assertType('CustomCollection', CustomCollection::make([new User])); assertType('Illuminate\Support\Collection', CustomCollection::make([new User])->toBase()); From b93207fcb4ae740e2cddc6f5d26a61da2a5de268 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 23 Aug 2021 10:35:52 +0000 Subject: [PATCH 15/23] Apply fixes from StyleCI --- src/Illuminate/Collections/helpers.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Collections/helpers.php b/src/Illuminate/Collections/helpers.php index 44743ad73586..45fc6d40510d 100644 --- a/src/Illuminate/Collections/helpers.php +++ b/src/Illuminate/Collections/helpers.php @@ -9,7 +9,7 @@ * * @template TKey of array-key * @template TValue - * + * * @param \Illuminate\Contracts\Support\Arrayable|iterable|null $value * @return \Illuminate\Support\Collection */ From f409bbbc9dfaad9568b490ff0a805fe3f2b87b60 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 24 Aug 2021 15:17:05 +0100 Subject: [PATCH 16/23] Adds work in progress regarding generic collections --- src/Illuminate/Collections/Collection.php | 12 +- src/Illuminate/Collections/Enumerable.php | 26 +-- .../Collections/Traits/EnumeratesValues.php | 10 +- .../Contracts/Queue/QueueableCollection.php | 4 +- .../Database/Eloquent/Collection.php | 121 ++++++++------ src/Illuminate/Database/Eloquent/Model.php | 2 +- types/Database/Eloquent/Collection.php | 153 ++++++++++++++++++ types/Support/Collection.php | 3 + 8 files changed, 252 insertions(+), 79 deletions(-) create mode 100644 types/Database/Eloquent/Collection.php diff --git a/src/Illuminate/Collections/Collection.php b/src/Illuminate/Collections/Collection.php index 2e177f804646..c5cdf2381327 100644 --- a/src/Illuminate/Collections/Collection.php +++ b/src/Illuminate/Collections/Collection.php @@ -166,8 +166,8 @@ public function collapse() * Determine if an item exists in the collection. * * @param (callable(TValue): bool)|TValue|string $key - * @param TValue|string|null $operator - * @param TValue|null $value + * @param mixed $operator + * @param mixed $value * @return bool */ public function contains($key, $operator = null, $value = null) @@ -378,7 +378,7 @@ public function first(callable $callback = null, $default = null) * Get a flattened array of the items in the collection. * * @param int $depth - * @return static + * @return static */ public function flatten($depth = INF) { @@ -637,7 +637,7 @@ public function last(callable $callback = null, $default = null) /** * Get the values of a given key. * - * @param string|array $value + * @param string|array $value * @param string|null $key * @return static */ @@ -1135,8 +1135,8 @@ public function splitIn($numberOfGroups) * Get the first item in the collection, but only if exactly one item exists. Otherwise, throw an exception. * * @param (callable(TValue, TKey): bool)|string $key - * @param TValue|string|null $operator - * @param TValue|null $value + * @param mixed $operator + * @param mixed $value * @return TValue * * @throws \Illuminate\Support\ItemNotFoundException diff --git a/src/Illuminate/Collections/Enumerable.php b/src/Illuminate/Collections/Enumerable.php index 7ca78f77c9b7..c16e23077a6b 100644 --- a/src/Illuminate/Collections/Enumerable.php +++ b/src/Illuminate/Collections/Enumerable.php @@ -116,9 +116,9 @@ public function collapse(); /** * Alias for the "contains" method. * - * @param (callable(TValue): bool)|TValue|string $key - * @param TValue|string|null $operator - * @param TValue|null $value + * @param (callable(TValue, TKey): bool)|TValue|string $key + * @param mixed $operator + * @param mixed $value * @return bool */ public function some($key, $operator = null, $value = null); @@ -143,9 +143,9 @@ public function avg($callback = null); /** * Determine if an item exists in the enumerable. * - * @param (callable(TValue): bool)|TValue|string $key - * @param TValue|string|null $operator - * @param TValue|null $value + * @param (callable(TValue, TKey): bool)|TValue|string $key + * @param mixed $operator + * @param mixed $value * @return bool */ public function contains($key, $operator = null, $value = null); @@ -264,8 +264,8 @@ public function eachSpread(callable $callback); * Determine if all items pass the given truth test. * * @param (callable(TValue, TKey): bool)|TValue|string $key - * @param TValue|string|null $operator - * @param TValue|null $value + * @param mixed $operator + * @param mixed $value * @return bool */ public function every($key, $operator = null, $value = null); @@ -746,8 +746,8 @@ public function forPage($page, $perPage); * Partition the collection into two arrays using the given callback or key. * * @param (callable(TValue, TKey): bool)|TValue|string $key - * @param TValue|string|null $operator - * @param TValue|null $value + * @param mixed $operator + * @param mixed $value * @return array> */ public function partition($key, $operator = null, $value = null); @@ -867,8 +867,8 @@ public function split($numberOfGroups); * Get the first item in the collection, but only if exactly one item exists. Otherwise, throw an exception. * * @param (callable(TValue, TKey): bool)|string $key - * @param TValue|string|null $operator - * @param TValue|null $value + * @param mixed $operator + * @param mixed $value * @return TValue * * @throws \Illuminate\Support\ItemNotFoundException @@ -997,7 +997,7 @@ public function pipe(callable $callback); /** * Get the values of a given key. * - * @param string|array $value + * @param string|array $value * @param string|null $key * @return static */ diff --git a/src/Illuminate/Collections/Traits/EnumeratesValues.php b/src/Illuminate/Collections/Traits/EnumeratesValues.php index e8318cfacdf0..37a0c8f5b4a8 100644 --- a/src/Illuminate/Collections/Traits/EnumeratesValues.php +++ b/src/Illuminate/Collections/Traits/EnumeratesValues.php @@ -174,9 +174,9 @@ public function average($callback = null) /** * Alias for the "contains" method. * - * @param (callable(TValue): bool)|TValue|string $key - * @param TValue|string|null $operator - * @param TValue|null $value + * @param (callable(TValue, TKey): bool)|TValue|string $key + * @param mixed $operator + * @param mixed $value * @return bool */ public function some($key, $operator = null, $value = null) @@ -277,8 +277,8 @@ public function eachSpread(callable $callback) * Determine if all items pass the given truth test. * * @param (callable(TValue, TKey): bool)|TValue|string $key - * @param TValue|string|null $operator - * @param TValue|null $value + * @param mixed $operator + * @param mixed $value * @return bool */ public function every($key, $operator = null, $value = null) diff --git a/src/Illuminate/Contracts/Queue/QueueableCollection.php b/src/Illuminate/Contracts/Queue/QueueableCollection.php index 7f1ea19c5436..750d10d4b088 100644 --- a/src/Illuminate/Contracts/Queue/QueueableCollection.php +++ b/src/Illuminate/Contracts/Queue/QueueableCollection.php @@ -14,14 +14,14 @@ public function getQueueableClass(); /** * Get the identifiers for all of the entities. * - * @return array + * @return array */ public function getQueueableIds(); /** * Get the relationships of the entities being queued. * - * @return array + * @return array */ public function getQueueableRelations(); diff --git a/src/Illuminate/Database/Eloquent/Collection.php b/src/Illuminate/Database/Eloquent/Collection.php index ff9b2747fe3e..39672138f5e8 100755 --- a/src/Illuminate/Database/Eloquent/Collection.php +++ b/src/Illuminate/Database/Eloquent/Collection.php @@ -10,14 +10,22 @@ use Illuminate\Support\Str; use LogicException; +/** + * @template TKey of array-key + * @template TModel of \Illuminate\Database\Eloquent\Model + * + * @extends \Illuminate\Support\Collection + */ class Collection extends BaseCollection implements QueueableCollection { /** * Find a model in the collection by key. * + * @template TFindDefault + * * @param mixed $key - * @param mixed $default - * @return \Illuminate\Database\Eloquent\Model|static|null + * @param TFindDefault $default + * @return static|TModel|TFindDefault */ public function find($key, $default = null) { @@ -45,7 +53,7 @@ public function find($key, $default = null) /** * Load a set of relationships onto the collection. * - * @param array|string $relations + * @param array|string $relations * @return $this */ public function load($relations) @@ -66,9 +74,9 @@ public function load($relations) /** * Load a set of aggregations over relationship's column onto the collection. * - * @param array|string $relations + * @param array|string $relations * @param string $column - * @param string $function + * @param string|null $function * @return $this */ public function loadAggregate($relations, $column, $function = null) @@ -101,7 +109,7 @@ public function loadAggregate($relations, $column, $function = null) /** * Load a set of relationship counts onto the collection. * - * @param array|string $relations + * @param array|string $relations * @return $this */ public function loadCount($relations) @@ -112,7 +120,7 @@ public function loadCount($relations) /** * Load a set of relationship's max column values onto the collection. * - * @param array|string $relations + * @param array|string $relations * @param string $column * @return $this */ @@ -124,7 +132,7 @@ public function loadMax($relations, $column) /** * Load a set of relationship's min column values onto the collection. * - * @param array|string $relations + * @param array|string $relations * @param string $column * @return $this */ @@ -136,7 +144,7 @@ public function loadMin($relations, $column) /** * Load a set of relationship's column summations onto the collection. * - * @param array|string $relations + * @param array|string $relations * @param string $column * @return $this */ @@ -148,7 +156,7 @@ public function loadSum($relations, $column) /** * Load a set of relationship's average column values onto the collection. * - * @param array|string $relations + * @param array|string $relations * @param string $column * @return $this */ @@ -160,7 +168,7 @@ public function loadAvg($relations, $column) /** * Load a set of related existences onto the collection. * - * @param array|string $relations + * @param array|string $relations * @return $this */ public function loadExists($relations) @@ -171,7 +179,7 @@ public function loadExists($relations) /** * Load a set of relationships onto the collection if they are not already eager loaded. * - * @param array|string $relations + * @param array|string $relations * @return $this */ public function loadMissing($relations) @@ -245,7 +253,7 @@ protected function loadMissingRelation(self $models, array $path) * Load a set of relationships onto the mixed relationship collection. * * @param string $relation - * @param array $relations + * @param array $relations * @return $this */ public function loadMorph($relation, $relations) @@ -266,7 +274,7 @@ public function loadMorph($relation, $relations) * Load a set of relationship counts onto the mixed relationship collection. * * @param string $relation - * @param array $relations + * @param array $relations * @return $this */ public function loadMorphCount($relation, $relations) @@ -286,7 +294,7 @@ public function loadMorphCount($relation, $relations) /** * Determine if a key exists in the collection. * - * @param mixed $key + * @param (callable(TModel, TKey): bool)|TModel|string $key * @param mixed $operator * @param mixed $value * @return bool @@ -311,7 +319,7 @@ public function contains($key, $operator = null, $value = null) /** * Get the array of primary keys. * - * @return array + * @return array */ public function modelKeys() { @@ -323,8 +331,8 @@ public function modelKeys() /** * Merge the collection with the given items. * - * @param \ArrayAccess|array $items - * @return static + * @param iterable $items + * @return static */ public function merge($items) { @@ -340,8 +348,10 @@ public function merge($items) /** * Run a map over each of the items. * - * @param callable $callback - * @return \Illuminate\Support\Collection|static + * @template TMapValue + * + * @param callable(TModel, TKey): TMapValue $callback + * @return \Illuminate\Support\Collection|static */ public function map(callable $callback) { @@ -357,8 +367,11 @@ public function map(callable $callback) * * The callback should return an associative array with a single key / value pair. * - * @param callable $callback - * @return \Illuminate\Support\Collection|static + * @template TMapWithKeysKey of array-key + * @template TMapWithKeysValue + * + * @param callable(TModel, TKey): array $callback + * @return \Illuminate\Support\Collection|static */ public function mapWithKeys(callable $callback) { @@ -372,8 +385,8 @@ public function mapWithKeys(callable $callback) /** * Reload a fresh model instance from the database for all the entities. * - * @param array|string $with - * @return static + * @param array|string $with + * @return static */ public function fresh($with = []) { @@ -400,8 +413,8 @@ public function fresh($with = []) /** * Diff the collection with the given items. * - * @param \ArrayAccess|array $items - * @return static + * @param iterable $items + * @return static */ public function diff($items) { @@ -421,8 +434,8 @@ public function diff($items) /** * Intersect the collection with the given items. * - * @param \ArrayAccess|array $items - * @return static + * @param iterable $items + * @return static */ public function intersect($items) { @@ -446,9 +459,9 @@ public function intersect($items) /** * Return only unique items from the collection. * - * @param string|callable|null $key + * @param (callable(TModel, TKey): bool)|string|null $key * @param bool $strict - * @return static + * @return static */ public function unique($key = null, $strict = false) { @@ -462,8 +475,8 @@ public function unique($key = null, $strict = false) /** * Returns only the models from the collection with the specified keys. * - * @param mixed $keys - * @return static + * @param array|null $keys + * @return static */ public function only($keys) { @@ -479,8 +492,8 @@ public function only($keys) /** * Returns all models in the collection except the models with specified keys. * - * @param mixed $keys - * @return static + * @param array|null $keys + * @return static */ public function except($keys) { @@ -492,7 +505,7 @@ public function except($keys) /** * Make the given, typically visible, attributes hidden across the entire collection. * - * @param array|string $attributes + * @param array|string $attributes * @return $this */ public function makeHidden($attributes) @@ -503,7 +516,7 @@ public function makeHidden($attributes) /** * Make the given, typically hidden, attributes visible across the entire collection. * - * @param array|string $attributes + * @param array|string $attributes * @return $this */ public function makeVisible($attributes) @@ -514,7 +527,7 @@ public function makeVisible($attributes) /** * Append an attribute across the entire collection. * - * @param array|string $attributes + * @param array|string $attributes * @return $this */ public function append($attributes) @@ -525,8 +538,8 @@ public function append($attributes) /** * Get a dictionary keyed by primary keys. * - * @param \ArrayAccess|array|null $items - * @return array + * @param iterable|null $items + * @return array */ public function getDictionary($items = null) { @@ -548,9 +561,9 @@ public function getDictionary($items = null) /** * Get an array with the values of a given key. * - * @param string|array $value + * @param string|array $value * @param string|null $key - * @return \Illuminate\Support\Collection + * @return \Illuminate\Support\Collection */ public function pluck($value, $key = null) { @@ -560,7 +573,7 @@ public function pluck($value, $key = null) /** * Get the keys of the collection items. * - * @return \Illuminate\Support\Collection + * @return \Illuminate\Support\Collection */ public function keys() { @@ -570,8 +583,10 @@ public function keys() /** * Zip the collection together with one or more arrays. * - * @param mixed ...$items - * @return \Illuminate\Support\Collection + * @template TZipValue + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable ...$items + * @return \Illuminate\Support\Collection> */ public function zip($items) { @@ -581,7 +596,7 @@ public function zip($items) /** * Collapse the collection of items into a single array. * - * @return \Illuminate\Support\Collection + * @return \Illuminate\Support\Collection */ public function collapse() { @@ -592,7 +607,7 @@ public function collapse() * Get a flattened array of the items in the collection. * * @param int $depth - * @return \Illuminate\Support\Collection + * @return \Illuminate\Support\Collection */ public function flatten($depth = INF) { @@ -602,7 +617,7 @@ public function flatten($depth = INF) /** * Flip the items in the collection. * - * @return \Illuminate\Support\Collection + * @return \Illuminate\Support\Collection */ public function flip() { @@ -612,9 +627,11 @@ public function flip() /** * Pad collection to the specified length with a value. * + * @template TPadValue + * * @param int $size - * @param mixed $value - * @return \Illuminate\Support\Collection + * @param TPadValue $value + * @return \Illuminate\Support\Collection */ public function pad($size, $value) { @@ -625,7 +642,7 @@ public function pad($size, $value) * Get the comparison function to detect duplicates. * * @param bool $strict - * @return \Closure + * @return callable(TValue, TValue): bool */ protected function duplicateComparator($strict) { @@ -661,7 +678,7 @@ public function getQueueableClass() /** * Get the identifiers for all of the entities. * - * @return array + * @return array */ public function getQueueableIds() { @@ -677,7 +694,7 @@ public function getQueueableIds() /** * Get the relationships of the entities being queued. * - * @return array + * @return array */ public function getQueueableRelations() { diff --git a/src/Illuminate/Database/Eloquent/Model.php b/src/Illuminate/Database/Eloquent/Model.php index 2222ffe1a46e..8fee029b5357 100644 --- a/src/Illuminate/Database/Eloquent/Model.php +++ b/src/Illuminate/Database/Eloquent/Model.php @@ -526,7 +526,7 @@ public static function onWriteConnection() * Get all of the models from the database. * * @param array|mixed $columns - * @return \Illuminate\Database\Eloquent\Collection|static[] + * @return \Illuminate\Database\Eloquent\Collection */ public static function all($columns = ['*']) { diff --git a/types/Database/Eloquent/Collection.php b/types/Database/Eloquent/Collection.php new file mode 100644 index 000000000000..74a42c8358b7 --- /dev/null +++ b/types/Database/Eloquent/Collection.php @@ -0,0 +1,153 @@ +', $collection); + +assertType('Illuminate\Database\Eloquent\Collection|User|null', $collection->find(1)); +assertType('Illuminate\Database\Eloquent\Collection|string|User', $collection->find(1, 'string')); + +assertType('Illuminate\Database\Eloquent\Collection', $collection->load('string')); +assertType('Illuminate\Database\Eloquent\Collection', $collection->load(['string'])); + +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadAggregate('string', 'string')); +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadAggregate(['string'], 'string')); +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadAggregate(['string'], 'string', 'string')); + +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadCount('string')); +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadCount(['string'])); + +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadMax('string', 'string')); +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadMax(['string'], 'string')); + +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadMin('string', 'string')); +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadMin(['string'], 'string')); + +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadSum('string', 'string')); +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadSum(['string'], 'string')); + +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadAvg('string', 'string')); +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadAvg(['string'], 'string')); + +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadExists('string')); +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadExists(['string'])); + +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadMissing('string')); +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadMissing(['string'])); + +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadMorph('string', ['string'])); + +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadMorphCount('string', ['string'])); + +assertType('bool', $collection->contains(function ($user) { + assertType('User', $user); + + return true; +})); +assertType('bool', $collection->contains('string', '=', 'string')); + +assertType('array', $collection->modelKeys()); + +assertType('Illuminate\Database\Eloquent\Collection', $collection->merge($collection)); +assertType('Illuminate\Database\Eloquent\Collection', $collection->merge([new User])); + +assertType( + 'Illuminate\Support\Collection', + $collection->map(function ($user, $int) { + assertType('User', $user); + assertType('int', $int); + + return new User; + }) +); + +assertType( + 'Illuminate\Support\Collection', + $collection->mapWithKeys(function ($user, $int) { + assertType('User', $user); + assertType('int', $int); + + return [new User]; + }) +); +assertType( + 'Illuminate\Support\Collection', + $collection->mapWithKeys(function ($user, $int) { + return ['string' => new User]; + }) +); + +assertType( + 'Illuminate\Database\Eloquent\Collection', + $collection->fresh() +); +assertType( + 'Illuminate\Database\Eloquent\Collection', + $collection->fresh('string') +); +assertType( + 'Illuminate\Database\Eloquent\Collection', + $collection->fresh(['string']) +); + +assertType('Illuminate\Database\Eloquent\Collection', $collection->diff($collection)); +assertType('Illuminate\Database\Eloquent\Collection', $collection->diff([new User])); + +assertType('Illuminate\Database\Eloquent\Collection', $collection->intersect($collection)); +assertType('Illuminate\Database\Eloquent\Collection', $collection->intersect([new User])); + +assertType('Illuminate\Database\Eloquent\Collection', $collection->unique()); +assertType('Illuminate\Database\Eloquent\Collection', $collection->unique(function ($user, $int) { + assertType('User', $user); + assertType('int', $int); + + return true; +})); +assertType('Illuminate\Database\Eloquent\Collection', $collection->unique('string')); + +assertType('Illuminate\Database\Eloquent\Collection', $collection->only(null)); +assertType('Illuminate\Database\Eloquent\Collection', $collection->only(['string'])); + +assertType('Illuminate\Database\Eloquent\Collection', $collection->except(null)); +assertType('Illuminate\Database\Eloquent\Collection', $collection->except(['string'])); + +assertType('Illuminate\Database\Eloquent\Collection', $collection->makeHidden('string')); +assertType('Illuminate\Database\Eloquent\Collection', $collection->makeHidden(['string'])); + +assertType('Illuminate\Database\Eloquent\Collection', $collection->makeVisible('string')); +assertType('Illuminate\Database\Eloquent\Collection', $collection->makeVisible(['string'])); + +assertType('Illuminate\Database\Eloquent\Collection', $collection->append('string')); +assertType('Illuminate\Database\Eloquent\Collection', $collection->append(['string'])); + +assertType('array', $collection->getDictionary()); +assertType('array', $collection->getDictionary($collection)); +assertType('array', $collection->getDictionary([new User])); + +assertType('Illuminate\Support\Collection', $collection->pluck('string')); +assertType('Illuminate\Support\Collection', $collection->pluck(['string'])); + +assertType('Illuminate\Support\Collection', $collection->keys()); + +assertType('Illuminate\Support\Collection>', $collection->zip([1])); +assertType('Illuminate\Support\Collection>', $collection->zip(['string'])); + +assertType('Illuminate\Support\Collection', $collection->collapse()); + +assertType('Illuminate\Support\Collection', $collection->flatten()); +assertType('Illuminate\Support\Collection', $collection->flatten(4)); + +assertType('Illuminate\Support\Collection', $collection->flip()); + +assertType('Illuminate\Support\Collection', $collection->pad(2, 0)); +assertType('Illuminate\Support\Collection', $collection->pad(2, 'string')); + +assertType('array', $collection->getQueueableIds()); + +assertType('array', $collection->getQueueableRelations()); diff --git a/types/Support/Collection.php b/types/Support/Collection.php index 2e36227252f2..a1cf2c82feee 100644 --- a/types/Support/Collection.php +++ b/types/Support/Collection.php @@ -368,6 +368,9 @@ class User extends Authenticatable return false; }, 'string')); +assertType('Illuminate\Support\Collection', $collection->flatten()); +assertType('Illuminate\Support\Collection', $collection::make(['string' => 'string'])->flatten(4)); + assertType('User|null', $collection->firstWhere('string', 'string')); assertType('User|null', $collection->firstWhere('string', 'string', 'string')); From d6c42910a032f1647f07d42dc800d8be22a77963 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 25 Aug 2021 11:31:07 +0100 Subject: [PATCH 17/23] Remove work on Models --- src/Illuminate/Database/Eloquent/Model.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Database/Eloquent/Model.php b/src/Illuminate/Database/Eloquent/Model.php index 8fee029b5357..2222ffe1a46e 100644 --- a/src/Illuminate/Database/Eloquent/Model.php +++ b/src/Illuminate/Database/Eloquent/Model.php @@ -526,7 +526,7 @@ public static function onWriteConnection() * Get all of the models from the database. * * @param array|mixed $columns - * @return \Illuminate\Database\Eloquent\Collection + * @return \Illuminate\Database\Eloquent\Collection|static[] */ public static function all($columns = ['*']) { From dfcadb6979146f819fd2e029009d228d8c30b654 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 25 Aug 2021 11:33:34 +0100 Subject: [PATCH 18/23] Revert "Remove work on Models" This reverts commit d6c42910a032f1647f07d42dc800d8be22a77963. --- src/Illuminate/Database/Eloquent/Model.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Database/Eloquent/Model.php b/src/Illuminate/Database/Eloquent/Model.php index 2222ffe1a46e..8fee029b5357 100644 --- a/src/Illuminate/Database/Eloquent/Model.php +++ b/src/Illuminate/Database/Eloquent/Model.php @@ -526,7 +526,7 @@ public static function onWriteConnection() * Get all of the models from the database. * * @param array|mixed $columns - * @return \Illuminate\Database\Eloquent\Collection|static[] + * @return \Illuminate\Database\Eloquent\Collection */ public static function all($columns = ['*']) { From 449a374fec233ea4568e509bc97485ac04d16a32 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 25 Aug 2021 11:34:02 +0100 Subject: [PATCH 19/23] Removes `prefer-lowest` --- .github/workflows/types.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/types.yml b/.github/workflows/types.yml index 2b8d7f717ed3..9ae536fb88c3 100644 --- a/.github/workflows/types.yml +++ b/.github/workflows/types.yml @@ -14,7 +14,7 @@ jobs: fail-fast: true matrix: php: ['8.0'] - stability: [prefer-lowest, prefer-stable] + stability: [prefer-stable] include: - php: '8.1' flags: "--ignore-platform-req=php" From 4aa15abde8e6f2cd1919dc3bdc4dff95ca50a898 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 25 Aug 2021 11:36:32 +0100 Subject: [PATCH 20/23] Removes non needed code on CI job Co-authored-by: Dries Vints --- .github/workflows/types.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/types.yml b/.github/workflows/types.yml index 9ae536fb88c3..31b13134caf6 100644 --- a/.github/workflows/types.yml +++ b/.github/workflows/types.yml @@ -14,13 +14,11 @@ jobs: fail-fast: true matrix: php: ['8.0'] - stability: [prefer-stable] include: - php: '8.1' flags: "--ignore-platform-req=php" - stability: prefer-stable - name: PHP ${{ matrix.php }} - ${{ matrix.stability }} + name: PHP ${{ matrix.php }} steps: - name: Checkout code @@ -38,7 +36,7 @@ jobs: with: timeout_minutes: 5 max_attempts: 5 - command: composer update --${{ matrix.stability }} --prefer-dist --no-interaction --no-progress ${{ matrix.flags }} + command: composer update --prefer-stable --prefer-dist --no-interaction --no-progress ${{ matrix.flags }} - name: Execute type checking continue-on-error: ${{ matrix.php > 8 }} From 8c14725ee49e4c56527edead337546cb484072eb Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 25 Aug 2021 14:23:38 +0100 Subject: [PATCH 21/23] Fixes `Eloquent\Collection::load` types --- src/Illuminate/Database/Eloquent/Collection.php | 2 +- types/Database/Eloquent/Collection.php | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Illuminate/Database/Eloquent/Collection.php b/src/Illuminate/Database/Eloquent/Collection.php index 39672138f5e8..9214f2c33e3d 100755 --- a/src/Illuminate/Database/Eloquent/Collection.php +++ b/src/Illuminate/Database/Eloquent/Collection.php @@ -53,7 +53,7 @@ public function find($key, $default = null) /** * Load a set of relationships onto the collection. * - * @param array|string $relations + * @param array|string $relations * @return $this */ public function load($relations) diff --git a/types/Database/Eloquent/Collection.php b/types/Database/Eloquent/Collection.php index 74a42c8358b7..a386c9365290 100644 --- a/types/Database/Eloquent/Collection.php +++ b/types/Database/Eloquent/Collection.php @@ -15,6 +15,9 @@ class User extends Authenticatable assertType('Illuminate\Database\Eloquent\Collection', $collection->load('string')); assertType('Illuminate\Database\Eloquent\Collection', $collection->load(['string'])); +assertType('Illuminate\Database\Eloquent\Collection', $collection->load(['string' => function ($query) { + // assertType('\Illuminate\Database\Query\Builder', $query); +}])); assertType('Illuminate\Database\Eloquent\Collection', $collection->loadAggregate('string', 'string')); assertType('Illuminate\Database\Eloquent\Collection', $collection->loadAggregate(['string'], 'string')); From f7430a9ac353179760ac243e4a379c68f0eb7894 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 25 Aug 2021 14:23:56 +0100 Subject: [PATCH 22/23] Adds work in progress regarding generic collections --- .../Collections/Traits/EnumeratesValues.php | 15 +++++++----- types/Support/Collection.php | 24 +++++++++++++++---- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/src/Illuminate/Collections/Traits/EnumeratesValues.php b/src/Illuminate/Collections/Traits/EnumeratesValues.php index 37a0c8f5b4a8..09487fd2ad0d 100644 --- a/src/Illuminate/Collections/Traits/EnumeratesValues.php +++ b/src/Illuminate/Collections/Traits/EnumeratesValues.php @@ -704,7 +704,7 @@ public function pipe(callable $callback) /** * Pass the collection into a new class. * - * @param string $class + * @param string-class $class * @return mixed */ public function pipeInto($class) @@ -736,9 +736,12 @@ public function reduce(callable $callback, $initial = null) /** * Reduce an associative collection to a single value. * - * @param callable $callback - * @param mixed $initial - * @return mixed + * @template TReduceWithKeysInitial + * @template TReduceWithKeysReturnType + * + * @param callable(TReduceWithKeysInitial|TReduceWithKeysReturnType, TValue): TReduceWithKeysReturnType $callback + * @param TReduceWithKeysInitial $initial + * @return TReduceWithKeysReturnType */ public function reduceWithKeys(callable $callback, $initial = null) { @@ -821,7 +824,7 @@ public function collect() /** * Get the collection of items as a plain array. * - * @return array + * @return array */ public function toArray() { @@ -833,7 +836,7 @@ public function toArray() /** * Convert the object into something JSON serializable. * - * @return array + * @return array */ #[\ReturnTypeWillChange] public function jsonSerialize() diff --git a/types/Support/Collection.php b/types/Support/Collection.php index a1cf2c82feee..c11e5f40c6b0 100644 --- a/types/Support/Collection.php +++ b/types/Support/Collection.php @@ -535,7 +535,6 @@ class User extends Authenticatable return 1; })); - assertType('int', $collection ->reduce(function ($int, $user) { assertType('User', $user); @@ -544,6 +543,21 @@ class User extends Authenticatable return 1; }, 0)); +assertType('int', $collection + ->reduceWithKeys(function ($null, $user) { + assertType('User', $user); + assertType('int|null', $null); + + return 1; + })); +assertType('int', $collection + ->reduceWithKeys(function ($int, $user) { + assertType('User', $user); + assertType('int', $int); + + return 1; + }, 0)); + assertType('Illuminate\Support\Collection', $collection::make([1])->replace([1])); assertType('Illuminate\Support\Collection', $collection->replace([new User])); @@ -687,6 +701,8 @@ class User extends Authenticatable return 1; })); +assertType('mixed', $collection->pipeInto(User::class)); + assertType('Illuminate\Support\Collection', $collection->make(['string' => 'string'])->pluck('string')); assertType('Illuminate\Support\Collection', $collection->make(['string' => 'string'])->pluck('string', 'string')); @@ -834,9 +850,9 @@ class CustomCollection extends Collection $collection->offsetUnset(0); unset($collection[0]); -assertType('array', $collection->toArray()); -assertType('array', collect(['string' => 'string'])->toArray()); -assertType('array', collect([1, 2])->toArray()); +assertType('array', $collection->toArray()); +assertType('array', collect(['string' => 'string'])->toArray()); +assertType('array', collect([1, 2])->toArray()); assertType('ArrayIterator', $collection->getIterator()); foreach ($collection as $int => $user) { From 1c4f459f1d78bf5c4f1bd445a54253953d063ef7 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Thu, 26 Aug 2021 15:05:03 +0100 Subject: [PATCH 23/23] Fixes `Eloquent\Collection::load` related methods --- .../Database/Eloquent/Collection.php | 20 ++++++------- types/Database/Eloquent/Collection.php | 30 +++++++++++++++++++ 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/src/Illuminate/Database/Eloquent/Collection.php b/src/Illuminate/Database/Eloquent/Collection.php index 9214f2c33e3d..be2fcc60c688 100755 --- a/src/Illuminate/Database/Eloquent/Collection.php +++ b/src/Illuminate/Database/Eloquent/Collection.php @@ -74,7 +74,7 @@ public function load($relations) /** * Load a set of aggregations over relationship's column onto the collection. * - * @param array|string $relations + * @param array|string $relations * @param string $column * @param string|null $function * @return $this @@ -109,7 +109,7 @@ public function loadAggregate($relations, $column, $function = null) /** * Load a set of relationship counts onto the collection. * - * @param array|string $relations + * @param array|string $relations * @return $this */ public function loadCount($relations) @@ -120,7 +120,7 @@ public function loadCount($relations) /** * Load a set of relationship's max column values onto the collection. * - * @param array|string $relations + * @param array|string $relations * @param string $column * @return $this */ @@ -132,7 +132,7 @@ public function loadMax($relations, $column) /** * Load a set of relationship's min column values onto the collection. * - * @param array|string $relations + * @param array|string $relations * @param string $column * @return $this */ @@ -144,7 +144,7 @@ public function loadMin($relations, $column) /** * Load a set of relationship's column summations onto the collection. * - * @param array|string $relations + * @param array|string $relations * @param string $column * @return $this */ @@ -156,7 +156,7 @@ public function loadSum($relations, $column) /** * Load a set of relationship's average column values onto the collection. * - * @param array|string $relations + * @param array|string $relations * @param string $column * @return $this */ @@ -168,7 +168,7 @@ public function loadAvg($relations, $column) /** * Load a set of related existences onto the collection. * - * @param array|string $relations + * @param array|string $relations * @return $this */ public function loadExists($relations) @@ -179,7 +179,7 @@ public function loadExists($relations) /** * Load a set of relationships onto the collection if they are not already eager loaded. * - * @param array|string $relations + * @param array|string $relations * @return $this */ public function loadMissing($relations) @@ -253,7 +253,7 @@ protected function loadMissingRelation(self $models, array $path) * Load a set of relationships onto the mixed relationship collection. * * @param string $relation - * @param array $relations + * @param array $relations * @return $this */ public function loadMorph($relation, $relations) @@ -274,7 +274,7 @@ public function loadMorph($relation, $relations) * Load a set of relationship counts onto the mixed relationship collection. * * @param string $relation - * @param array $relations + * @param array $relations * @return $this */ public function loadMorphCount($relation, $relations) diff --git a/types/Database/Eloquent/Collection.php b/types/Database/Eloquent/Collection.php index a386c9365290..f1b3527005ad 100644 --- a/types/Database/Eloquent/Collection.php +++ b/types/Database/Eloquent/Collection.php @@ -22,31 +22,61 @@ class User extends Authenticatable assertType('Illuminate\Database\Eloquent\Collection', $collection->loadAggregate('string', 'string')); assertType('Illuminate\Database\Eloquent\Collection', $collection->loadAggregate(['string'], 'string')); assertType('Illuminate\Database\Eloquent\Collection', $collection->loadAggregate(['string'], 'string', 'string')); +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadAggregate(['string' => function ($query) { + // assertType('\Illuminate\Database\Query\Builder', $query); +}], 'string', 'string')); assertType('Illuminate\Database\Eloquent\Collection', $collection->loadCount('string')); assertType('Illuminate\Database\Eloquent\Collection', $collection->loadCount(['string'])); +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadCount(['string' => function ($query) { + // assertType('\Illuminate\Database\Query\Builder', $query); +}])); assertType('Illuminate\Database\Eloquent\Collection', $collection->loadMax('string', 'string')); assertType('Illuminate\Database\Eloquent\Collection', $collection->loadMax(['string'], 'string')); +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadMax(['string' => function ($query) { + // assertType('\Illuminate\Database\Query\Builder', $query); +}], 'string')); assertType('Illuminate\Database\Eloquent\Collection', $collection->loadMin('string', 'string')); assertType('Illuminate\Database\Eloquent\Collection', $collection->loadMin(['string'], 'string')); +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadMin(['string' => function ($query) { + // assertType('\Illuminate\Database\Query\Builder', $query); +}], 'string')); assertType('Illuminate\Database\Eloquent\Collection', $collection->loadSum('string', 'string')); assertType('Illuminate\Database\Eloquent\Collection', $collection->loadSum(['string'], 'string')); +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadSum(['string' => function ($query) { + // assertType('\Illuminate\Database\Query\Builder', $query); +}], 'string')); assertType('Illuminate\Database\Eloquent\Collection', $collection->loadAvg('string', 'string')); assertType('Illuminate\Database\Eloquent\Collection', $collection->loadAvg(['string'], 'string')); +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadAvg(['string' => function ($query) { + // assertType('\Illuminate\Database\Query\Builder', $query); +}], 'string')); assertType('Illuminate\Database\Eloquent\Collection', $collection->loadExists('string')); assertType('Illuminate\Database\Eloquent\Collection', $collection->loadExists(['string'])); +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadExists(['string' => function ($query) { + // assertType('\Illuminate\Database\Query\Builder', $query); +}])); assertType('Illuminate\Database\Eloquent\Collection', $collection->loadMissing('string')); assertType('Illuminate\Database\Eloquent\Collection', $collection->loadMissing(['string'])); +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadMissing(['string' => function ($query) { + // assertType('\Illuminate\Database\Query\Builder', $query); +}])); assertType('Illuminate\Database\Eloquent\Collection', $collection->loadMorph('string', ['string'])); +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadMorph('string', ['string' => function ($query) { + // assertType('\Illuminate\Database\Query\Builder', $query); +}])); assertType('Illuminate\Database\Eloquent\Collection', $collection->loadMorphCount('string', ['string'])); +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadMorphCount('string', ['string' => function ($query) { + // assertType('\Illuminate\Database\Query\Builder', $query); +}])); assertType('bool', $collection->contains(function ($user) { assertType('User', $user);