PHPackages                             le0daniel/typescript-schema - 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. le0daniel/typescript-schema

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

le0daniel/typescript-schema
===========================

2.0.0(1y ago)0758↓50%MITPHPPHP ^8.3

Since Jul 17Pushed 1y ago1 watchersCompare

[ Source](https://github.com/le0daniel/typescript-schema)[ Packagist](https://packagist.org/packages/le0daniel/typescript-schema)[ RSS](/packages/le0daniel-typescript-schema/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (3)Dependencies (2)Versions (4)Used By (0)

Typescript Schema
=================

[](#typescript-schema)

This is a simple library with no outside dependencies that generates a typescript schema out of your defined schema. This can help creating tight contracts in rest APIs for your input and output, without the need and complexity of using GraphQL or similar.

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

[](#installation)

`composer require le0daniel/typescript-schema`

Getting started
---------------

[](#getting-started)

Define your schema for input or output.

```
use TypescriptSchema\Definition\Schema;

$userType = Schema::object([
    'name' => Schema::string()->nonEmpty()->minLength(1)->maxLength(255),
    'email' => Schema::string()->email()->endsWith('.com')
]);

$userType->toDefinition()->input() // JsonSchema => ['type' => 'object', 'properties' => ['name' => ['type' => 'string'], 'email' => ['type' => 'string']], (...)]
$userType->toDefinition()->output() // JsonSchema => ['type' => 'object', 'properties' => ['name' => ['type' => 'string'], 'email' => ['type' => 'string']], (...)]

$schema = Schema::make($userType);
$schema->parse(['name' => 'The name', 'email' => 'email@test.com', 'other']);
// => Result 'The name', 'email' => 'email@test.com']>

$schema->parse(['name' => 'The name', 'email' => 'INVALID', 'other']);
// => Result

$schema->parseOrFail(['name' => 'The name', 'email' => 'INVALID', 'other']);
// => throws ParsingException

$schema->parseOrFail(['name' => 'The name', 'email' => 'email@test.com', 'other']);
// => ['name' => 'The name', 'email' => 'email@test.com']
```

Difference between parse and serialize
--------------------------------------

[](#difference-between-parse-and-serialize)

The main difference is that parsing will cast some values to PHP types, whereas serialize will return a format that is JSON serializable. Use parse if you have unknown input that should be used in a PHP, use serialize if the result should be Json Serialized.

Parsing: Takes input and returns PHP Types where possible (Enums, DateTimeImmutable) Serializing: Takes input and serializes it to JsonSerializable format. Example (Enum =&gt; Enum-&gt;name, DateTimeImmutable =&gt; DateTimeImmutable-&gt;format(...))

Value Object Casting
--------------------

[](#value-object-casting)

It is possible to cast values as value objects. This is done using reflection. In case the object has a constructor, the constructor is used. Otherwise, a new object is created and the properties are set. Important, the latter case does not work with readonly properties. You can customize the casting logic by having a `public static function cast()` set on your class. In this case, this method is called with the raw data. No other logic applies.

```
use TypescriptSchema\Utils\Typescript;
use TypescriptSchema\Definition\Schema;
use TypescriptSchema\Data\Options;

$schema = Schema::object(['name' => Schema::string()])->toSchema();
$result = $schema->parse(['name' => 'Test name'])->castInto(Person::class);
```

Typescript Support
------------------

[](#typescript-support)

By default, the toDefinition() method generates Json Schema output. You can use it to create a valid typescript declaration using the Typescript utility.

```
use TypescriptSchema\Utils\Typescript;
use TypescriptSchema\Definition\Schema;

$schema = Schema::make(
    Schema::object([
        'id' => Schema::int()->min(1),
        'name' => Schema::string(),
    ])
);

Typescript::fromJsonSchema($schema->toDefinition()->output());
// => {id:number;name:string}
```

Primitive types
---------------

[](#primitive-types)

- String `Schema::string() | StringType::make()`
- Int `Schema::float() | FloatType::make()`
- Float `Schema::float() | FloatType::make()`
- Boolean `Schema::boolean() | BoolType::make()`
- Literal `Schema::literal('book') | LiteralType::make('book')`
- DateTime `Schema::dateTime(format) | DateTimeType::make(format)`
- Any `Schema::any() | AnyType::make()`
- Enum `Schema::enum(class) | EnumType::make(class)`

### Coercion of values

[](#coercion-of-values)

By default, type checks are strict. Meaning passing a number to a String type will result in a failure. You can use coercion to make it less strict as following: `Schema::string()->coerce()`. This is available for all primitive types and tries to parse the value from any input.

This works as following for the different primitives:

- String: `(string) $value`
- Int: `(int) $value`
- Float: `(float) $value`
- Boolean: `Accepts: 0, 1, '0', '1', 'true', 'false'`

### String

[](#string)

The string type supports following default validations:

- min (min amount of characters, including) `Schema::string()->min(5)`
- max (max amount of characters, including) `Schema::string()->max(5)`
- endsWith `Schema::string()->endsWith('.test')`
- startsWith `Schema::string()->startsWith('Hello: ')`
- notEmpty `Schema::string()->notEmpty()`
- regex `Schema::string()->regex('/[a-z]+/')`
- alphaNumeric `Schema::string()->alphaNumeric()`
- email `Schema::string()->email()`

### Int

[](#int)

The int type supports following validations:

- min `Schema::int()->min(5, including: true)`
- max `Schema::int()->max(5, including: true)`

### DateTime

[](#datetime)

The DateTime primitive accepts by default a string (in given format, defaults to `DateTime::ATOM`) or an instance of the `DateTimeInterface`. All instances are cast into a DateTimeImmutable.

By default, the output type is as following: `{date: string, timezone_type: number, timezone: string}`. This is the default JsonSerialization of a DateTime class in PHP.

For output, it is useful to use `Schema::dateTime()->asFormattedString()`, which will return the formatted string instead of the DateTimeImmutableInstance.

### Enum

[](#enum)

An Enum type expects the input to be the Enum instance or a string with the name of the enum. In serialization mode, the Enum name is outputted (`Enum::CASE->name`).

```
use TypescriptSchema\Definition\Schema;

enum MyEnum {
    case SUCCESS;
    case FAILURE;
}

$type = Schema::enum(MyEnum::class);

$type->toDefinition()->input()                // => 'SUCCESS'|'FAILURE'
$type->toDefinition()->output()               // => 'SUCCESS'|'FAILURE'

// Parsing always returns the Enum case itself.
Schema::make($type)->parse(MyEnum::SUCCESS) // => MyEnum::SUCCESS
Schema::make($type)->parse('SUCCESS') // => MyEnum::SUCCESS

// Parsing as strings always returns the string with the enum name.
Schema::make($type)->serialize('SUCCESS') // => 'SUCCESS'
Schema::make($type)->serialize(MyEnum::SUCCESS) // => 'SUCCESS'
```

### Any

[](#any)

Both the unknown and any type pass all data through in the form it was before.

```
use TypescriptSchema\Definition\Schema;

$type = Schema::make(Schema::any());
$type->parse(null) // => Result
$type->parse('string') // => Result
$type->parse((object) []) // => Result
$type->parse([]) // => Result
```

---

Complex Types
-------------

[](#complex-types)

Complex types build up on the primitives and add functionality around them. Following complex types are supported:

- Object
- Array
- Record
- Tuple
- Union / DiscriminatedUnion

### Object

[](#object)

Represents an Object in json with key value pairs that are known and typed. Objects are defined by an associative array in PHP, where the key needs to be a string and the value a Type or a Field.

```
use TypescriptSchema\Definition\Schema;
use \TypescriptSchema\Utils\Typescript;

$user = Schema::object([
    'name' => Schema::string(),
]);

Typescript::fromJsonSchema($user->toDefinition()->input())  // => {name: string;}
Typescript::fromJsonSchema($user->toDefinition()->output()) // => {name: string;}
```

By default, resolving a value to a field is done by checking if the key exists in the array or a property of an object.

In some cases, you might want to customize this resolution (Ex: you have a computed output field).

```
use TypescriptSchema\Definition\Complex\Field;use TypescriptSchema\Definition\Schema;
use TypescriptSchema\Definition\Schema;

$user = Schema::object([
    'fullName' => Field::ofType(Schema::string())
        ->resolvedBy(fn(User $user): string => $user->fullName()),
]);
```

Using fields adds more benefits. You can describe the field or even deprecate it. This is especially useful for output types that serialize your Data.

```
use TypescriptSchema\Definition\Complex\Field;
use TypescriptSchema\Definition\Schema;

$user = Schema::object([
    'fullName' => Field::ofType(Schema::string())
        ->resolvedBy(fn(User $user): string => $user->fullName())
        ->describe('The combination of title, first and last name')
        ->deprecated('Use lastName, firstName and title to compute manually', DateTime::createFromFormat('Y-m-d', '2024-05-25')),
]);
```

Extending
---------

[](#extending)

You can define your own custom types, by implementing the Type interface. Two methods need to be implemented:

- `public function toDefinition(): SchemaDefinition;`
- `public function parse(mixed $value, Context $context): mixed;`

The definition method should define the Json Schema for input and output, whereas the parse function should parse the given value into the correct type. It MUST NOT throw any exception. In case of failure, `Value::INVALID` should be returned and all issues added to the context.

If your type needs to serialize values in a specific way you have to additionally implement the `SerializesOutputValue`interface.

Remember, the types should be immutable.

Following utilities (traits) are available for types to use:

- Coerce: Enables `->coerce()`
- HasDefaultValue: `->defaultValue()`
- Nullable: Enables `->nullable()`
- Refinable: Enables `->refine()`
- Transformable: Enables `->transform()`
- Validators: Enables Closure Validators to be used easily.

Example

```
use TypescriptSchema\Contracts\Type;
use TypescriptSchema\Definition\Shared\Nullable;
use TypescriptSchema\Definition\Shared\Refinable;
use TypescriptSchema\Definition\Shared\Transformable;
use TypescriptSchema\Definition\Shared\Validators;
use TypescriptSchema\Helpers\Context;
use TypescriptSchema\Data\Enum\Value;
use TypescriptSchema\Exceptions\Issue;

final class MyCustomType implements Type {
    use Nullable, Refinable, Transformable, Validators;

    public function toDefinition(): SchemaDefinition {
        return new \TypescriptSchema\Data\Schema\Definition(
            ['type' => 'string'],
            ['type' => 'string'],
        );
    }

    public function serializeValue(mixed $value, Context $context): Value|string {
        if (!is_string($value)) {
            $context->addIssue(Issue::custom('Value must be a string.'));
            return Value::INVALID;
        }

        if (!$this->runValidators($value, $context)) {
            return Value::INVALID;
        }

        return $value;
    }

    public function minLength(): MyCustomType {
        return $this->addValidator(function (string $value) {
            return strlen($value) > 5;
        });
    }
}
```

###  Health Score

33

—

LowBetter than 75% of packages

Maintenance42

Moderate activity, may be stable

Popularity16

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity57

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% 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

Every ~56 days

Total

3

Last Release

557d ago

Major Versions

1.0.2 → 2.0.02024-11-07

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/22875367?v=4)[Leo Daniel A](/maintainers/leodaniel)[@leodaniel](https://github.com/leodaniel)

---

Top Contributors

[![le0daniel](https://avatars.githubusercontent.com/u/11374766?v=4)](https://github.com/le0daniel "le0daniel (95 commits)")

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/le0daniel-typescript-schema/health.svg)

```
[![Health](https://phpackages.com/badges/le0daniel-typescript-schema/health.svg)](https://phpackages.com/packages/le0daniel-typescript-schema)
```

PHPackages © 2026

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