Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions bin/NarrowingGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ public static function generateSetNarrowing(string $collectionContent, array $bl
return self::generateNarrowing($collectionContent, 'Collection', [
': Collection' => ': Set',
'Collection<E>' => 'Set<E>',
'Collection<(E is null ? never : E)>' => 'Set<(E is null ? never : E)>',
'Collection<R>' => 'Set<R>',
'Collection<mixed>' => 'Set<mixed>',
'Collection<T>' => 'Set<T>',
Expand Down Expand Up @@ -231,6 +232,7 @@ public static function generateImmutableSetNarrowing(string $immutableCollection
return self::generateNarrowing($immutableCollectionContent, 'ImmutableCollection', [
': ImmutableCollection' => ': ImmutableSet',
'ImmutableCollection<E>' => 'ImmutableSet<E>',
'ImmutableCollection<(E is null ? never : E)>' => 'ImmutableSet<(E is null ? never : E)>',
'ImmutableCollection<E|NE>' => 'ImmutableSet<E|NE>',
'ImmutableCollection<R>' => 'ImmutableSet<R>',
'ImmutableCollection<mixed>' => 'ImmutableSet<mixed>',
Expand Down Expand Up @@ -285,6 +287,7 @@ public static function generateListNarrowing(string $collectionContent, array $b
return self::generateNarrowing($collectionContent, 'Collection', [
': Collection' => ': ListInterface',
'Collection<E>' => 'ListInterface<E>',
'Collection<(E is null ? never : E)>' => 'ListInterface<(E is null ? never : E)>',
'Collection<R>' => 'ListInterface<R>',
'Collection<mixed>' => 'ListInterface<mixed>',
'Collection<T>' => 'ListInterface<T>',
Expand Down Expand Up @@ -314,6 +317,7 @@ public static function generateImmutableCollectionNarrowing(string $collectionCo
return self::generateNarrowing($collectionContent, 'Collection', [
': Collection' => ': ImmutableCollection',
'Collection<E>' => 'ImmutableCollection<E>',
'Collection<(E is null ? never : E)>' => 'ImmutableCollection<(E is null ? never : E)>',
'Collection<R>' => 'ImmutableCollection<R>',
'Collection<mixed>' => 'ImmutableCollection<mixed>',
'Collection<T>' => 'ImmutableCollection<T>',
Expand All @@ -340,6 +344,7 @@ public static function generateWritableCollectionTransformationNarrowing(string
return self::generateNarrowing($collectionContent, 'Collection', [
': Collection' => ': ImmutableCollection',
'Collection<E>' => 'ImmutableCollection<E>',
'Collection<(E is null ? never : E)>' => 'ImmutableCollection<(E is null ? never : E)>',
'Collection<R>' => 'ImmutableCollection<R>',
'Collection<mixed>' => 'ImmutableCollection<mixed>',
'Collection<T>' => 'ImmutableCollection<T>',
Expand All @@ -366,6 +371,7 @@ public static function generateWritableListTransformationNarrowing(string $colle
return self::generateNarrowing($collectionContent, 'Collection', [
': Collection' => ': ImmutableList',
'Collection<E>' => 'ImmutableList<E>',
'Collection<(E is null ? never : E)>' => 'ImmutableList<(E is null ? never : E)>',
'Collection<R>' => 'ImmutableList<R>',
'Collection<mixed>' => 'ImmutableList<mixed>',
'Collection<T>' => 'ImmutableList<T>',
Expand All @@ -392,6 +398,7 @@ public static function generateWritableSetTransformationNarrowing(string $collec
return self::generateNarrowing($collectionContent, 'Collection', [
': Collection' => ': ImmutableSet',
'Collection<E>' => 'ImmutableSet<E>',
'Collection<(E is null ? never : E)>' => 'ImmutableSet<(E is null ? never : E)>',
'Collection<R>' => 'ImmutableSet<R>',
'Collection<mixed>' => 'ImmutableSet<mixed>',
'Collection<T>' => 'ImmutableSet<T>',
Expand Down Expand Up @@ -419,6 +426,7 @@ public static function generateWritableMapTransformationNarrowing(string $mapCon
': Map' => ': ImmutableMap',
'Map<K,V>' => 'ImmutableMap<K,V>',
'Map<K, V>' => 'ImmutableMap<K, V>',
'Map<K, (V is null ? never : V)>' => 'ImmutableMap<K, (V is null ? never : V)>',
'Map<NK,V>' => 'ImmutableMap<NK,V>',
'Map<NK, V>' => 'ImmutableMap<NK, V>',
'Map<K,NV>' => 'ImmutableMap<K,NV>',
Expand All @@ -441,6 +449,7 @@ public static function generateImmutableListNarrowing(string $immutableCollectio
return self::generateNarrowing($immutableCollectionContent, 'ImmutableCollection', [
': ImmutableCollection' => ': ImmutableList',
'ImmutableCollection<E>' => 'ImmutableList<E>',
'ImmutableCollection<(E is null ? never : E)>' => 'ImmutableList<(E is null ? never : E)>',
'ImmutableCollection<E|NE>' => 'ImmutableList<E|NE>',
'ImmutableCollection<R>' => 'ImmutableList<R>',
'ImmutableCollection<mixed>' => 'ImmutableList<mixed>',
Expand Down Expand Up @@ -544,6 +553,7 @@ public static function generateImmutableMapNarrowing(string $mapContent, array $
': Map' => ': ImmutableMap',
'Map<K,V>' => 'ImmutableMap<K,V>',
'Map<K, V>' => 'ImmutableMap<K, V>',
'Map<K, (V is null ? never : V)>' => 'ImmutableMap<K, (V is null ? never : V)>',
'Map<NK,V>' => 'ImmutableMap<NK,V>',
'Map<NK, V>' => 'ImmutableMap<NK, V>',
'Map<K,NV>' => 'ImmutableMap<K,NV>',
Expand Down
2 changes: 1 addition & 1 deletion src/Collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ public function filter(Closure $predicate): Collection;
/**
* Filter non-null elements.
*
* @return Collection<E>
* @return Collection<(E is null ? never : E)>
*/
#[NoDiscard]
public function filterNotNull(): Collection;
Expand Down
2 changes: 1 addition & 1 deletion src/ImmutableCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ public function filter(Closure $predicate): ImmutableCollection;
/**
* Filter non-null elements.
*
* @return ImmutableCollection<E>
* @return ImmutableCollection<(E is null ? never : E)>
*/
#[NoDiscard]
public function filterNotNull(): ImmutableCollection;
Expand Down
2 changes: 1 addition & 1 deletion src/List/ImmutableList.php
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ public function filter(Closure $predicate): ImmutableList;
/**
* Filter non-null elements.
*
* @return ImmutableList<E>
* @return ImmutableList<(E is null ? never : E)>
*/
#[NoDiscard]
public function filterNotNull(): ImmutableList;
Expand Down
2 changes: 1 addition & 1 deletion src/List/ListInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ public function filter(Closure $predicate): ListInterface;
/**
* Filter non-null elements.
*
* @return ListInterface<E>
* @return ListInterface<(E is null ? never : E)>
*/
#[NoDiscard]
public function filterNotNull(): ListInterface;
Expand Down
4 changes: 2 additions & 2 deletions src/List/ListLogic.php
Original file line number Diff line number Diff line change
Expand Up @@ -179,9 +179,9 @@ public function filter(Closure $predicate): ImmutableList
/**
* {@inheritDoc}
*
* @return ImmutableList<E>
* @return ImmutableList<(E is null ? never : E)>
*/
#[NoDiscard]
#[NoDiscard] // @phpstan-ignore conditionalType.subjectNotFound (in classes with a concrete E the conditional subject is already substituted)
public function filterNotNull(): ImmutableList
{
return $this->newCollectionOf(new FilterOperation($this->store)->byValue(fn ($v) => $v !== null));
Expand Down
2 changes: 1 addition & 1 deletion src/List/WritableList.php
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ public function filter(Closure $predicate): ImmutableList;
/**
* Filter non-null elements.
*
* @return ImmutableList<E>
* @return ImmutableList<(E is null ? never : E)>
*/
#[NoDiscard]
public function filterNotNull(): ImmutableList;
Expand Down
2 changes: 1 addition & 1 deletion src/Map/ImmutableMap.php
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ public function filterValues(Closure $predicate): ImmutableMap;
/**
* Creates a new map excluding entries with null values.
*
* @return ImmutableMap<K,V> New map without null values.
* @return ImmutableMap<K, (V is null ? never : V)> New map without null values.
*/
#[NoDiscard]
public function filterValuesNotNull(): ImmutableMap;
Expand Down
2 changes: 1 addition & 1 deletion src/Map/Map.php
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ public function filterValues(Closure $predicate): Map;
/**
* Creates a new map excluding entries with null values.
*
* @return Map<K,V> New map without null values.
* @return Map<K, (V is null ? never : V)> New map without null values.
*/
#[NoDiscard]
public function filterValuesNotNull(): Map;
Expand Down
4 changes: 2 additions & 2 deletions src/Map/MapLogic.php
Original file line number Diff line number Diff line change
Expand Up @@ -223,9 +223,9 @@ public function filterValues(Closure $predicate): ImmutableMap
/**
* {@inheritDoc}
*
* @return ImmutableMap<K,V>
* @return ImmutableMap<K, (V is null ? never : V)>
*/
#[NoDiscard]
#[NoDiscard] // @phpstan-ignore conditionalType.subjectNotFound (in classes with a concrete V the conditional subject is already substituted)
public function filterValuesNotNull(): ImmutableMap
{
return $this->newMapOf(
Expand Down
2 changes: 1 addition & 1 deletion src/Map/WritableMap.php
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ public function filterValues(Closure $predicate): ImmutableMap;
/**
* Creates a new map excluding entries with null values.
*
* @return ImmutableMap<K,V> New map without null values.
* @return ImmutableMap<K, (V is null ? never : V)> New map without null values.
*/
#[NoDiscard]
public function filterValuesNotNull(): ImmutableMap;
Expand Down
2 changes: 1 addition & 1 deletion src/Set/ImmutableSet.php
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ public function filter(Closure $predicate): ImmutableSet;
/**
* Filter non-null elements.
*
* @return ImmutableSet<E>
* @return ImmutableSet<(E is null ? never : E)>
*/
#[NoDiscard]
public function filterNotNull(): ImmutableSet;
Expand Down
2 changes: 1 addition & 1 deletion src/Set/Set.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public function filter(Closure $predicate): Set;
/**
* Filter non-null elements.
*
* @return Set<E>
* @return Set<(E is null ? never : E)>
*/
#[NoDiscard]
public function filterNotNull(): Set;
Expand Down
4 changes: 2 additions & 2 deletions src/Set/SetLogic.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@ public function filter(Closure $predicate): ImmutableSet
/**
* {@inheritDoc}
*
* @return ImmutableSet<E>
* @return ImmutableSet<(E is null ? never : E)>
*/
#[NoDiscard]
#[NoDiscard] // @phpstan-ignore conditionalType.subjectNotFound, conditionalType.alwaysFalse (in classes with a concrete or non-nullable E the conditional is already decided)
public function filterNotNull(): ImmutableSet
{
return $this->newCollectionOf(new FilterOperation($this->store)->byValue(fn ($v) => $v !== null));
Expand Down
2 changes: 1 addition & 1 deletion src/Set/WritableSet.php
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ public function filter(Closure $predicate): ImmutableSet;
/**
* Filter non-null elements.
*
* @return ImmutableSet<E>
* @return ImmutableSet<(E is null ? never : E)>
*/
#[NoDiscard]
public function filterNotNull(): ImmutableSet;
Expand Down
2 changes: 1 addition & 1 deletion src/WritableCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ public function filter(Closure $predicate): ImmutableCollection;
/**
* Filter non-null elements.
*
* @return ImmutableCollection<E>
* @return ImmutableCollection<(E is null ? never : E)>
*/
#[NoDiscard]
public function filterNotNull(): ImmutableCollection;
Expand Down
38 changes: 38 additions & 0 deletions tests/Type/FilterNotNullTypeTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

/**
* This file is part of the Noctud Collection.
* Copyright (c) Noctud.dev
*/

declare(strict_types=1);

namespace Noctud\Collection\Tests\Type;

use Noctud\Collection\Collection;

use function Noctud\Collection\listOf;
use function Noctud\Collection\mutableListOf;
use function Noctud\Collection\mutableSetOf;
use function Noctud\Collection\mutableStringMapOf;
use function Noctud\Collection\setOf;
use function Noctud\Collection\stringMapOf;
use function PHPStan\Testing\assertType;

// filterNotNull() removes null from the element type (issue #16).
assertType('Noctud\Collection\List\ImmutableList<int>', listOf([1, null])->filterNotNull());
assertType('Noctud\Collection\List\ImmutableList<int>', mutableListOf([1, null])->filterNotNull());
assertType('Noctud\Collection\Set\ImmutableSet<int>', setOf([1, null])->filterNotNull());
assertType('Noctud\Collection\Set\ImmutableSet<int>', mutableSetOf([1, null])->filterNotNull());

// Also through the base Collection interface.
/** @var Collection<string|null> $c */
$c = listOf(['a', null]);
assertType('Noctud\Collection\Collection<string>', $c->filterNotNull());

// A collection without null keeps its element type unchanged.
assertType('Noctud\Collection\List\ImmutableList<int>', listOf([1, 2])->filterNotNull());

// filterValuesNotNull() removes null from the value type, keys are preserved.
assertType('Noctud\Collection\Map\ImmutableMap<string, int>', stringMapOf(['a' => 1, 'b' => null])->filterValuesNotNull());
assertType('Noctud\Collection\Map\ImmutableMap<string, int>', mutableStringMapOf(['a' => 1, 'b' => null])->filterValuesNotNull());
Loading