{"id":13828547,"url":"https://github.com/spatie/typed","last_synced_at":"2025-07-09T06:32:03.970Z","repository":{"id":54693794,"uuid":"134744208","full_name":"spatie/typed","owner":"spatie","description":"Improvements to PHP's type system in userland: generics, typed lists, tuples and structs","archived":true,"fork":false,"pushed_at":"2021-01-26T19:04:24.000Z","size":137,"stargazers_count":319,"open_issues_count":0,"forks_count":15,"subscribers_count":14,"default_branch":"master","last_synced_at":"2024-10-21T03:01:06.764Z","etag":null,"topics":["generics","structs","tuples","types"],"latest_commit_sha":null,"homepage":"https://spatie.be/en/opensource/php","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/spatie.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-05-24T16:50:28.000Z","updated_at":"2024-09-23T16:41:58.000Z","dependencies_parsed_at":"2022-08-14T00:10:50.430Z","dependency_job_id":null,"html_url":"https://github.com/spatie/typed","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spatie%2Ftyped","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spatie%2Ftyped/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spatie%2Ftyped/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spatie%2Ftyped/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/spatie","download_url":"https://codeload.github.com/spatie/typed/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225492420,"owners_count":17482869,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["generics","structs","tuples","types"],"created_at":"2024-08-04T09:02:51.652Z","updated_at":"2024-11-20T08:30:40.142Z","avatar_url":"https://github.com/spatie.png","language":"PHP","funding_links":[],"categories":["PHP"],"sub_categories":[],"readme":"# Improved PHP type system in userland\n\n[![Latest Version on Packagist](https://img.shields.io/packagist/v/spatie/typed.svg?style=flat-square)](https://packagist.org/packages/spatie/typed)\n[![Build Status](https://img.shields.io/travis/spatie/typed/master.svg?style=flat-square)](https://travis-ci.org/spatie/typed)\n[![StyleCI](https://github.styleci.io/repos/134744208/shield?branch=master)](https://github.styleci.io/repos/134744208)\n[![Quality Score](https://img.shields.io/scrutinizer/g/spatie/typed.svg?style=flat-square)](https://scrutinizer-ci.com/g/spatie/typed)\n[![Total Downloads](https://img.shields.io/packagist/dt/spatie/typed.svg?style=flat-square)](https://packagist.org/packages/spatie/typed)\n\nThis package is a mere proof of concept about what's possible in PHP's userland to improve type checking. \nIt adds support for type inference, generics, union types, typed lists, tuples and structs.\nBecause all is done in userland, there are limitations on what syntax is possible.\n\n## Support us\n\n[\u003cimg src=\"https://github-ads.s3.eu-central-1.amazonaws.com/typed.jpg?t=1\" width=\"419px\" /\u003e](https://spatie.be/github-ad-click/typed)\n\nWe invest a lot of resources into creating [best in class open source packages](https://spatie.be/open-source). You can support us by [buying one of our paid products](https://spatie.be/open-source/support-us).\n\nWe highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using. You'll find our address on [our contact page](https://spatie.be/about-us). We publish all received postcards on [our virtual postcard wall](https://spatie.be/open-source/postcards).\n\n## Installation\n\nYou can install the package via composer:\n\n```bash\ncomposer require spatie/typed\n```\n\n## Usage\n\n### Type inference\n\nBoth collections, tuples and structs support inferred types. \nThis means that all examples are also possible, without manually specifying types.\nFor example:\n\n```php\n// This collection will only allow objects of type `Post` in it.\n$postCollection = new Collection([new Post()]);\n\n// This tuple will keep its signature of (Point, Point).\n$vector = new Tuple(new Point(30, 5), new Point(120, 0));\n\n// This struct's fields are autmoatically type checked.\n$struct = [\n    'foo' =\u003e new Post(),\n    'bar' =\u003e function () {\n        // ...\n    },\n];\n```\n\nThe following examples all show the manual type configuration. \nThere are some cases where type inference falls short, and you have to fall back on manually defining them.\nYou might also prefer the manual approach, for clarity's sake.\n\nNote that type may be partially inferred. \nSome fields in tuples or structs may be type definitions, others may be real values.\nUninitialised types will throw an error on read. \n\n### Typed lists and collections:\n\n```php\n$list = new Collection(T::bool());\n\n$list[] = new Post(); // TypeError\n```\n\nIt's possible to directly initialise a collection with data after construction.\n\n```php\n$list = (new Collection(T::string()))-\u003eset(['a', 'b', 'c']);\n```\n\nThis package also provides some predefined lists, as shortcuts.\n\n```php\n$list = new IntegerList([1, 4]);\n\n$list[] = 'a'; // TypeError\n```\n\n### Generics:\n\nGeneric types wrap around classes, allowing you to not creating a custom type for every class.\n\n```php\n$postList = new Collection(T::generic(Post::class));\n\n$postList[] = 1; // TypeError\n```\n\n### Tuples:\n\n```php\n$point = new Tuple(T::float(), T::float());\n\n$point[0] = 1.5;\n$point[1] = 3;\n\n$point[0] = 'a'; // TypeError\n$point['a'] = 1; // TypeError\n$point[10] = 1; // TypeError\n```\n\nLike lists, a tuple can also be given some data after construction with the `set` function.\n\n```php\n$tuple = (new Tuple(T::string(), T::array()))-\u003eset('abc', []);\n```\n\n### Structs:\n\n```php\n$developer = new Struct([\n    'name' =\u003e T::string(),\n    'age' =\u003e T::int(),\n    'second_name' =\u003e T::nullable(T::string()),\n]);\n\n$developer['name'] = 'Brent';\n$developer['second_name'] = 'John';\n\n$developer-\u003eset([\n    'name' =\u003e 'BrenDt',\n    'age' =\u003e 23,\n    'second_name' =\u003e null,\n]);\n\necho $developer-\u003eage;\n\n$developer-\u003ename = 'Brent';\n\n$developer-\u003eage = 'abc' // TypeError\n$developer-\u003esomethingElse = 'abc' // TypeError\n```\n\n### Nullable type\n\nA nullable type can be defined in two, functionally identical, ways:\n\n```php\n$list1 = new Collection(T::int()-\u003enullable());\n\n$list2 = new Collection(T::nullable(T::int()));\n```\n\n### Union Type\n\nA union type means a collection of multiple types.\n\n```php\n$list = new Collection(T::union(T::int(), T::float()));\n\n$list[] = 1;\n$list[] = 1.1;\n\n$list[] = 'abc'; // TypeError\n```\n\nUnion types may also be nullable and contain generics.\n\n### What's not included:\n\n- Proper syntax.\n- IDE auto completion for generic types.\n- Prevention of type casting between scalar types.\n- Type hint generics in functions.\n\n## Creating your own types\n\nThe `GenericType` or `T::generic()` can be used to create structures of that type.\nIt is, however, also possible to create your own types without generics. \nLet's take the example of `Post`. The generic approach works without adding custom types.\n\n```php\n$postList = new Collection(T::generic(Post::class));\n\n$postList[] = new Post();\n$postList[] = 1; // TypeError \n```\n\nThe `generic` part can be skipped if you create your own type.\n\n```php\nuse Spatie\\Typed\\Type;\nuse Spatie\\Typed\\Types\\Nullable;\n\nclass PostType implements Type\n{\n    use Nullable;\n    \n    public function validate($post): Post\n    {\n        return $post;\n    }\n}\n```\n\nNow you can use `PostType` directly:\n\n```php\n$postList = new Collection(new PostType());\n```\n\nYou're also free to extend the `T` helper.\n\n```php\nclass T extends Spatie\\Typed\\T\n{\n    public static function post(): PostType\n    {\n        return new PostType();\n    }\n}\n\n// ...\n\n$postList = new Collection(T::post());\n```\n\nThe `Nullable` trait adds the following simple snippet, \nso that the type can be made nullable when used.\n\n```php\npublic function nullable(): NullType\n{\n    return new NullType($this);\n}\n```\n\n\u003e **Note:** It's recommended to also implement `__toString` in your own type classes. \n\n## Extending data structures\n\nYou're free to extend the existing data structures. \nFor example, you could make shorthand tuples like so:\n\n```php\nclass Coordinates extends Tuple\n{\n    public function __construct(int $x, int $y)\n    {\n        parent::__construct(T::int(), T::int());\n\n        $this[0] = $x;\n        $this[1] = $y;\n    }\n}\n```\n\n## Why did we build this?\n\nPHP has a very weak type system. \nThis is simultaneously a strength and a weakness. \nWeak type systems offer a very flexible development platform,\nwhile strong type systems can prevent certain bugs from happening at runtime.\n\nIn its current state, PHP's type system isn't ready for some of the features many want. \nTake, for example, a look at some RFC's proposing changes to the current type system.\n\n- Generics: [https://wiki.php.net/rfc/generics](https://wiki.php.net/rfc/generics)\n- Typed properties: [https://wiki.php.net/rfc/typed-properties](https://wiki.php.net/rfc/typed-properties)\n- Readonly properties: [https://wiki.php.net/rfc/readonly_properties](https://wiki.php.net/rfc/readonly_properties)\n\nSome of those are already declined because of runtime performance issues, or implementation difficulties.\nThis package is a thought experiment of what we could do if those features are implemented in PHP, usable with native syntax.\n\nFor example, the following syntax would be much more preferable over how this package does it.\n\n```php\n$postList = new Collection\u003cPost\u003e();\n\n// vs.\n\n$postList[] = new Collection(T::generic(Post::class));\n```\n\nAnyways, it's stuff to think about. \nAnd maybe PHP's type system is fine as it is now? \nYou can read more about type safety [on my blog](https://www.stitcher.io/blog/liskov-and-type-safety).\n\n## Contributing\n\nPlease see [CONTRIBUTING](CONTRIBUTING.md) for details.\n\n## Security\n\nIf you discover any security related issues, please email freek@spatie.be instead of using the issue tracker.\n\n## Postcardware\n\nYou're free to use this package, but if it makes it to your production environment we highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using.\n\nOur address is: Spatie, Kruikstraat 22, 2018 Antwerp, Belgium.\n\nWe publish all received postcards [on our company website](https://spatie.be/en/opensource/postcards).\n\n## Credits\n\n- [Brent Roose](https://github.com/brendt)\n- [All Contributors](../../contributors)\n\n## License\n\nThe MIT License (MIT). Please see [License File](LICENSE.md) for more information.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspatie%2Ftyped","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fspatie%2Ftyped","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspatie%2Ftyped/lists"}