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
8 changes: 8 additions & 0 deletions psalm.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@
</ignoreFiles>
</projectFiles>

<issueHandlers>
<UnusedClass>
<errorLevel type="suppress">
<directory name="tests"/>
</errorLevel>
</UnusedClass>
</issueHandlers>

<forbiddenFunctions>
<function name="dd"/>
<function name="die"/>
Expand Down
35 changes: 35 additions & 0 deletions src/IntMaskOfType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

declare(strict_types=1);

namespace Typhoon\Type;

/**
* @api
* @psalm-immutable
* @template-covariant TIntMask of positive-int
* @implements Type<TIntMask>
*/
final class IntMaskOfType implements Type
{
/**
* @var Type<TIntMask>
*/
public readonly Type $type;

/**
* @internal
* @psalm-internal Typhoon\Type
* @param Type<TIntMask> $type
*/
public function __construct(
Type $type,
) {
$this->type = $type;
}

public function accept(TypeVisitor $visitor): mixed
{
return $visitor->visitIntMaskOf($this);
}
}
35 changes: 35 additions & 0 deletions src/IntMaskType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

declare(strict_types=1);

namespace Typhoon\Type;

/**
* @api
* @psalm-immutable
* @template-covariant TIntMask of int<0, max>
* @implements Type<TIntMask>
*/
final class IntMaskType implements Type
{
/**
* @var non-empty-list<int<0, max>>
*/
public readonly array $ints;

/**
* @internal
* @psalm-internal Typhoon\Type
* @param non-empty-list<int<0, max>> $ints
*/
public function __construct(
array $ints,
) {
$this->ints = $ints;
}

public function accept(TypeVisitor $visitor): mixed
{
return $visitor->visitIntMask($this);
}
}
6 changes: 6 additions & 0 deletions src/TypeVisitor.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ public function visitLiteralInt(LiteralIntType $type): mixed;
/** @return TReturn */
public function visitIntRange(IntRangeType $type): mixed;

/** @return TReturn */
public function visitIntMask(IntMaskType $type): mixed;

/** @return TReturn */
public function visitIntMaskOf(IntMaskOfType $type): mixed;

/** @return TReturn */
public function visitInt(IntType $type): mixed;

Expand Down
24 changes: 24 additions & 0 deletions src/types.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

namespace Typhoon\Type;

use Psalm\Type\Atomic\TIntMask;

/**
* @api
*/
Expand Down Expand Up @@ -81,6 +83,28 @@ public static function positiveInt(): IntRangeType
return new IntRangeType(1);
}

/**
* @psalm-pure
* @no-named-arguments
* @param int<0, max> $int
* @param int<0, max> ...$ints
*/
public static function intMask(int $int, int ...$ints): IntMaskType
{
return new IntMaskType([$int, ...$ints]);
}

/**
* @psalm-pure
* @template TIntMask of positive-int
* @param Type<TIntMask> $type
* @return IntMaskOfType<TIntMask>
*/
public static function intMaskOf(Type $type): IntMaskOfType
{
return new IntMaskOfType($type);
}

/**
* @psalm-pure
*/
Expand Down
25 changes: 25 additions & 0 deletions tests/psalm/IntMaskOfTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

declare(strict_types=1);

namespace Typhoon\Type\IntMaskOfTest;

use Typhoon\Type\IntMaskOfType;
use Typhoon\Type\Type;
use function Typhoon\Type\extractType;

final class X
{
const A = 1;
const B = 2;
const C = 4;
}

/**
* @param Type<X::*> $constantType
*/
function testItPreservesPassedType(Type $constantType): void
{
/** @psalm-check-type-exact $_int = 1|2|4 */
$_int = extractType(new IntMaskOfType($constantType));
}
17 changes: 17 additions & 0 deletions tests/psalm/IntMaskTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace Typhoon\Type\IntMaskTest;

use Typhoon\Type\IntMaskType;
use function Typhoon\Type\extractType;

/**
* @param IntMaskType<int-mask<1, 2, 4>> $type
*/
function a(IntMaskType $type): void
{
/** @psalm-check-type-exact $_int = int-mask<1, 2, 4> */
$_int = extractType($type);
}