PHPackages                             j0hnys/trident-typed - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. j0hnys/trident-typed

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

j0hnys/trident-typed
====================

Improvements to PHP's type system in userland: generics, typed lists, tuples and structs

v0.1.0.0(6y ago)01282MITPHPPHP &gt;=7.0

Since Aug 9Pushed 6y ago1 watchersCompare

[ Source](https://github.com/j0hnys/trident-typed)[ Packagist](https://packagist.org/packages/j0hnys/trident-typed)[ Docs](https://github.com/j0hnys/trident-typed)[ RSS](/packages/j0hnys-trident-typed/feed)WikiDiscussions master Synced 3d ago

READMEChangelogDependenciesVersions (2)Used By (2)

Improved PHP type system in userland
====================================

[](#improved-php-type-system-in-userland)

This package is a mere proof of concept about what's possible in PHP's userland to improve type checking. It adds support for type inference, generics, union types, typed lists, tuples and structs. Because all is done in userland, there are limitations on what syntax is possible.

Installation
------------

[](#installation)

You can install the package via composer:

```
composer require j0hnys/trident-typed
```

Usage
-----

[](#usage)

### Type inference

[](#type-inference)

Both collections, tuples and structs support inferred types. This means that all examples are also possible, without manually specifying types. For example:

```
// This collection will only allow objects of type `Post` in it.
$postCollection = new Collection([new Post()]);

// This tuple will keep its signature of (Point, Point).
$vector = new Tuple(new Point(30, 5), new Point(120, 0));

// This struct's fields are autmoatically type checked.
$struct = [
    'foo' => new Post(),
    'bar' => function () {
        // ...
    },
];
```

The following examples all show the manual type configuration. There are some cases where type inference falls short, and you have to fall back on manually defining them. You might also prefer the manual approach, for clarity's sake.

Note that type may be partially inferred. Some fields in tuples or structs may be type definitions, others may be real values. Uninitialised types will throw an error on read.

### Typed lists and collections:

[](#typed-lists-and-collections)

```
$list = new Collection(T::bool());

$list[] = new Post(); // TypeError
```

It's possible to directly initialise a collection with data after construction.

```
$list = (new Collection(T::string()))->set(['a', 'b', 'c']);
```

This package also provides some predefined lists, as shortcuts.

```
$list = new IntegerList([1, 4]);

$list[] = 'a'; // TypeError
```

### Maps:

[](#maps)

```
$map = new Map(T::string(),T::integer());

$map['name'] = 1;

$map[] = new Post(); // TypeError
$map[] = 1; // TypeError
```

It's possible to directly initialise a collection with data after construction.

```
$map = (new Map(T::string(),T::integer()))->set(['a'=>2]);
```

### Generics:

[](#generics)

Generic types wrap around classes, allowing you to not creating a custom type for every class.

```
$postList = new Collection(T::generic(Post::class));

$postList[] = 1; // TypeError
```

### Tuples:

[](#tuples)

```
$point = new Tuple(T::float(), T::float());

$point[0] = 1.5;
$point[1] = 3;

$point[0] = 'a'; // TypeError
$point['a'] = 1; // TypeError
$point[10] = 1; // TypeError
```

Like lists, a tuple can also be given some data after construction with the `set` function.

```
$tuple = (new Tuple(T::string(), T::array()))->set('abc', []);
```

### Structs:

[](#structs)

```
$developer = new Struct([
    'name' => T::string(),
    'age' => T::int(),
    'second_name' => T::nullable(T::string()),
]);

$developer['name'] = 'Brent';
$developer['second_name'] = 'John';

$developer->set([
    'name' => 'BrenDt',
    'age' => 23,
    'second_name' => null,
]);

echo $developer->age;

$developer->name = 'Brent';

$developer->age = 'abc' // TypeError
$developer->somethingElse = 'abc' // TypeError
```

### Nullable type

[](#nullable-type)

A nullable type can be defined in two, functionally identical, ways:

```
$list1 = new Collection(T::int()->nullable());

$list2 = new Collection(T::nullable(T::int()));
```

### Union Type

[](#union-type)

A union type means a collection of multiple types.

```
$list = new Collection(T::union(T::int(), T::float()));

$list[] = 1;
$list[] = 1.1;

$list[] = 'abc'; // TypeError
```

Union types may also be nullable and contain generics.

### What's not included:

[](#whats-not-included)

- Proper syntax.
- IDE auto completion for generic types.
- Prevention of type casting between scalar types.
- Type hint generics in functions.

Creating your own types
-----------------------

[](#creating-your-own-types)

The `GenericType` or `T::generic()` can be used to create structures of that type. It is, however, also possible to create your own types without generics. Let's take the example of `Post`. The generic approach works without adding custom types.

```
$postList = new Collection(T::generic(Post::class));

$postList[] = new Post();
$postList[] = 1; // TypeError
```

The `generic` part can be skipped if you create your own type.

```
use J0hnys\Typed\Type;
use J0hnys\Typed\Types\Nullable;

class PostType implements Type
{
    use Nullable;

    public function validate($post): Post
    {
        return $post;
    }
}
```

Now you can use `PostType` directly:

```
$postList = new Collection(new PostType());
```

You're also free to extend the `T` helper.

```
class T extends J0hnys\Typed\T
{
    public static function post(): PostType
    {
        return new PostType();
    }
}

// ...

$postList = new Collection(T::post());
```

The `Nullable` trait adds the following simple snippet, so that the type can be made nullable when used.

```
public function nullable(): NullType
{
    return new NullType($this);
}
```

> **Note:** It's recommended to also implement `__toString` in your own type classes.

Extending data structures
-------------------------

[](#extending-data-structures)

You're free to extend the existing data structures. For example, you could make shorthand tuples like so:

```
class Coordinates extends Tuple
{
    public function __construct(int $x, int $y)
    {
        parent::__construct(T::int(), T::int());

        $this[0] = $x;
        $this[1] = $y;
    }
}
```

###  Health Score

25

—

LowBetter than 35% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity12

Limited adoption so far

Community18

Small or concentrated contributor base

Maturity44

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 54.1% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

2517d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/24209278?v=4)[j0hnys](/maintainers/j0hnys)[@j0hnys](https://github.com/j0hnys)

---

Top Contributors

[![brendt](https://avatars.githubusercontent.com/u/6905297?v=4)](https://github.com/brendt "brendt (73 commits)")[![freekmurze](https://avatars.githubusercontent.com/u/483853?v=4)](https://github.com/freekmurze "freekmurze (22 commits)")[![j0hnys](https://avatars.githubusercontent.com/u/24209278?v=4)](https://github.com/j0hnys "j0hnys (19 commits)")[![localheinz](https://avatars.githubusercontent.com/u/605483?v=4)](https://github.com/localheinz "localheinz (12 commits)")[![Furgas](https://avatars.githubusercontent.com/u/715407?v=4)](https://github.com/Furgas "Furgas (5 commits)")[![peter279k](https://avatars.githubusercontent.com/u/9021747?v=4)](https://github.com/peter279k "peter279k (2 commits)")[![BackEndTea](https://avatars.githubusercontent.com/u/14289961?v=4)](https://github.com/BackEndTea "BackEndTea (1 commits)")[![akoepcke](https://avatars.githubusercontent.com/u/5311185?v=4)](https://github.com/akoepcke "akoepcke (1 commits)")

---

Tags

typedgenericstuplestructstyped list

### Embed Badge

![Health badge](/badges/j0hnys-trident-typed/health.svg)

```
[![Health](https://phpackages.com/badges/j0hnys-trident-typed/health.svg)](https://phpackages.com/packages/j0hnys-trident-typed)
```

###  Alternatives

[gamez/typed-collection

Type-safe collections based on Laravel Collections

45362.9k](/packages/gamez-typed-collection)[selective/transformer

A strictly typed array transformer with dot-access, fluent interface and filters.

3819.1k1](/packages/selective-transformer)[selective/array-reader

A strictly typed array reader

1171.0k5](/packages/selective-array-reader)[rotexsoft/versatile-collections

A collection package that can be extended to implement things such as a Dependency Injection Container, RecordSet objects for housing database records, a bag of http cookies, or technically any collection of items that can be looped over and whose items can each be accessed using array-access syntax or object property syntax.

186.1k1](/packages/rotexsoft-versatile-collections)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
