typhoon / type
Typhoon Type System
Fund package maintenance!
www.tinkoff.ru/cf/5MqZQas2dk7
Installs: 5 682
Dependents: 7
Suggesters: 0
Security: 0
Stars: 53
Watchers: 5
Forks: 1
Open Issues: 0
Requires
- php: ^8.1
- typhoon/declaration-id: ^0.4
README
Typhoon Type is an object abstraction over the modern PHP type system. Use this library to build tools that work with sophisticated types.
Here are some examples of potential use-cases:
use Typhoon\Type\types; $data = (new MyAwesomeJsonDecoder())->decode( json: '[1, 0.5, "213"]', type: types::list(types::numeric), ); var_dump($data);
array(3) {
[0] => int(1)
[1] => float(0.5)
[2] => string(3) "213"
}
Or:
use Typhoon\Type\types; final readonly class GetUserResponse { /** * @param non-empty-string $name * @param 'user'|'admin' $group */ public function __construct( public Uuid $id, public string $name, public string $group, ) {} } echo (new MyAwesomeOpenApiGenerator())->generateSchema(types::object(GetUserResponse::class));
GetUserResponse: type: object properties: id: type: string format: uuid example: b609e6a9-bba6-4599-9faa-cc9977353bb4 name: type: string example: Hello world! group: type: string enum: [ user, admin ]
Installation
composer require typhoon/type
Constructing types
Typhoon types can be constructed via the Typhoon\Type\types
static factory. Let's express this monstrous type via
the Typhoon DSL:
array{
a: non-empty-string,
b?: int|float,
c: Traversable<numeric-string, false>,
d: callable(PDO::*, TSend#Generator=, scalar...): void,
...
}
use Typhoon\Type\types; $type = types::unsealedArrayShape([ 'a' => types::nonEmptyString, 'b' => types::optional(types::union(types::int, types::float)), 'c' => types::object(Traversable::class, [types::numericString, types::false]), 'd' => types::callable( parameters: [ types::classConstantMask(PDO::class), types::param(types::classTemplate(Generator::class, 'TSend'), hasDefault: true), types::param(types::scalar, variadic: true), ], return: types::void, ), ]);
As you can see, creating types in Typhoon is a lot of fun, especially if you work in IDE with autocompletion 😉
Design
Unlike other solutions, Typhoon Type does not expose concrete type classes in its API. Instead, it provides only
a common Type
interface, a type factory types
, and
a TypeVisitor
with destructurization.
This approach gives several advantages:
- The visitor has only a minimal subset of type methods that must be implemented when describing a type algebra. Complexity of the other types is hidden and can be completely ignored.
- Memory efficient enums can be used for all atomic types and for aliases of commonly used compound types.
- Using of downcasting via the
instanceof
operator is automatically discouraged, since allType
implementations are@internal
( see PHPStan: Why Is instanceof *Type Wrong and Getting Deprecated?).
Printing types
To cast any type to string, use the Typhoon\Type\stringify()
function:
use Typhoon\Type\types; use function Typhoon\Type\stringify; var_dump( stringify( types::Generator( key: types::nonNegativeInt, value: types::classTemplate(Foo::class, 'T'), send: types::scalar, ), ), ); // Generator<int<0, max>, T#Foo, scalar, mixed>
Comparing types
Typhoon team is currently working on a type comparator. Until it is released, you can use DefaultTypeVisitor for simple checks:
use Typhoon\Type\Type; use Typhoon\Type\types; use Typhoon\Type\Visitor\DefaultTypeVisitor; /** * @extends DefaultTypeVisitor<bool> */ final class BasicIntChecker extends DefaultTypeVisitor { public function int(Type $type, Type $minType, Type $maxType): mixed { return true; } public function intValue(Type $type, int $value): mixed { return true; } public function intMask(Type $type, Type $ofType): bool { return true; } protected function default(Type $type): bool { return false; } } var_dump(types::positiveInt->accept(new BasicIntChecker())); // true var_dump(types::callableString()->accept(new BasicIntChecker())); // false