Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(type): Notnull type optimizations #465

Merged
merged 1 commit into from
Apr 16, 2024
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
2 changes: 1 addition & 1 deletion docs/component/type.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
- [non_empty_dict](./../../src/Psl/Type/non_empty_dict.php#L18)
- [non_empty_string](./../../src/Psl/Type/non_empty_string.php#L14)
- [non_empty_vec](./../../src/Psl/Type/non_empty_vec.php#L16)
- [nonnull](./../../src/Psl/Type/nonnull.php#L16)
- [nonnull](./../../src/Psl/Type/nonnull.php#L18)
- [null](./../../src/Psl/Type/null.php#L14)
- [nullable](./../../src/Psl/Type/nullable.php#L16)
- [num](./../../src/Psl/Type/num.php#L14)
Expand Down
22 changes: 18 additions & 4 deletions src/Psl/Type/Internal/NonNullType.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,29 @@
final readonly class NonNullType extends Type\Type
{
/**
* @psalm-assert-if-true mixed $value
* @template T of mixed
*
* @param T|null $value
*
* @psalm-assert-if-true T $value
*
* @ara-assert-if-true nonnull $value
*
* @return ($value is null ? false : true)
*/
public function matches(mixed $value): bool
{
return null !== $value;
}

/**
* @template T of mixed
*
* @param T|null $value
*
* @ara-return nonnull
*
* @return mixed
* @return ($value is null ? never : T)
*/
public function coerce(mixed $value): mixed
{
Expand All @@ -42,13 +52,17 @@ public function coerce(mixed $value): mixed
}

/**
* @template T
*
* @param T|null $value
*
* @ara-assert nonnull $value
*
* @psalm-assert mixed $value
* @psalm-assert T $value
*
* @ara-return nonnull
*
* @return mixed
* @return ($value is null ? never : T)
*/
public function assert(mixed $value): mixed
{
Expand Down
4 changes: 3 additions & 1 deletion src/Psl/Type/nonnull.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@

namespace Psl\Type;

use Psl\Type\Internal\NonNullType;

/**
* @psalm-pure
*
* @psalm-suppress ImpureStaticVariable - The $instance is always the same and is considered pure.
*
* @ara-return TypeInterface<nonnull>
*
* @return TypeInterface<mixed>
* @return NonNullType
*/
function nonnull(): TypeInterface
{
Expand Down
64 changes: 64 additions & 0 deletions tests/static-analysis/Type/nonnull.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php

declare(strict_types=1);

namespace Psl\Tests\StaticAnalysis\Type;

use Psl\Type;

/**
* @throws Type\Exception\AssertException
*/
function returns_non_null_assertion(?string $state): string
{
return Type\nonnull()->assert($state);
}

/**
* @throws Type\Exception\AssertException
*/
function returns_non_null_assertion_asserted(?string $state): string
{
Type\nonnull()->assert($state);

return $state;
}

/**
* @throws Type\Exception\CoercionException
*/
function returns_non_null_coercion(?string $state): string
{
return Type\nonnull()->coerce($state);
}

/**
* @return true
*/
function returns_truthy_match(string $state): bool
{
return Type\nonnull()->matches($state);
}

/**
* @return false
*/
function returns_falsy_match(null $state = null): bool
{
return Type\nonnull()->matches($state);
}

/**
* @throws Type\Exception\CoercionException
*
* Wrapping it in container types still leeds to mixed (including null) return type.
* This won't be solvable until psalm supports a TNotNull type.
*
* @return array<'mightBeNull', mixed>
*/
function returns_mixed_in_shape(mixed $data): array
{
return Type\shape([
'mightBeNull' => Type\nonnull(),
])->coerce($data);
}