---
title: "Types"
description: "Compose strict validation, scalar conversion, collections, enums, dates and custom types"
type: "docs"
category: "doc"
tags: []
authors: [Anonymous]
date: "2026-10-26"
last_update: "2026-10-26"
time_minutes: 4
draft: false
unlisted: false
url: "https://www.derafu.dev/docs/data/value/types"
---

# ValueType

`ValueType<T>` validates or explicitly converts a value and returns it as `T`. PHPStan follows the generic return type through collection and union compositions.

```php
use Derafu\Value\ValueType;

ValueType::int()->parse(12);       // 12
ValueType::toInt()->parse('12.0'); // 12
ValueType::number()->parse(12.0);  // 12.0, unchanged
ValueType::toNumber()->parse(12.0); // 12
```

## Available Factories

| Factory | Contract |
| --- | --- |
| `mixed()` | Any value, including null. |
| `string()`, `int()`, `float()`, `bool()` | Exactly the indicated PHP type, without coercion. |
| `number()` | An existing integer or float, preserved unchanged. |
| `scalar()` | An existing boolean, integer, float or string. |
| `nonEmptyString()` | A string whose content is nonempty before and after PHP `trim()`. |
| `toInt()`, `toFloat()`, `toNumber()`, `toBool()`, `toString()` | Delegate to the corresponding ValueCaster method, excluding null. |
| `array()` | Any PHP array. |
| `map()` | An array with string keys only. |
| `list()` | An array with consecutive integer keys starting at zero. |
| `arrayOf($type)` | Any array whose values match the supplied type. |
| `mapOf($type)` | A string-keyed array whose values match the supplied type. |
| `listOf($type)` | A list whose values match the supplied type. |
| `literal($first, ...$others)` | One of the supplied boolean, integer or string values, compared strictly. |
| `instanceOf($class)` | An object of the given class or interface, returned unchanged. |
| `backedEnum($class)` | An existing case or an exact int/string backing value of the requested backed enum. |
| `dateTime()` | A DateTimeInterface, integer timestamp or non-blank date string interpreted by PHP. |
| `oneOf($first, ...$others)` | The first type that accepts the value. |

The primitive `float()` rejects integers even though `ValueCaster::toFloat()` can convert them. Use an explicit conversion type when conversion is intended.

## Nullability

Apply `nullable()` to accept null in addition to a type's ordinary values:

```php
use Derafu\Value\ValueType;

ValueType::int()->nullable()->parse(null);    // null
ValueType::toInt()->nullable()->parse(null);  // null
```

ValueCaster preserves null directly, but conversion types reject it by default. `ValueType::toInt()->parse(null)` throws `TypeMismatchException`. This keeps nullability explicit in a composed type.

## Collections

Empty collections are accepted. Lists require consecutive integer keys; maps require string keys; arbitrary arrays accept both. PHP converts some numeric string keys to integers, so `['123' => 'value']` is not a map, while `['01' => 'value']` is.

```php
use Derafu\Value\ValueType;

$type = ValueType::arrayOf(ValueType::mapOf(ValueType::toInt()));
$type->parse([3 => ['quantity' => '12.0'], 8 => ['quantity' => '2']]);
// [3 => ['quantity' => 12], 8 => ['quantity' => 2]]
```

Collections preserve their keys and report nested errors with paths such as `rows.8.quantity`. These factories describe element types; `mapOf()` does not define required or optional named fields.

## Unions and Literal Values

```php
use Derafu\Value\ValueType;

$type = ValueType::oneOf(ValueType::toInt(), ValueType::string());
$type->parse('12.0'); // 12
$type->parse('text'); // 'text'

ValueType::literal('asc', 'desc')->parse('asc'); // 'asc'
ValueType::literal(false)->parse(false);        // false; 0 would be rejected
```

Union order matters because conversion types can interpret the input. `oneOf()` tries the next candidate when the current candidate throws a translatable invalid-argument exception. It does not catch arbitrary runtime failures.

## Backed Enums

```php
use Derafu\Value\ValueType;

enum Status: string
{
    case Pending = 'pending';
    case Approved = 'approved';
}

$type = ValueType::backedEnum(Status::class);
$type->parse('approved');       // Status::Approved
$type->parse(Status::Pending);  // the same enum case
```

Int-backed enums accept integer backing values; string-backed enums accept strings. Numeric strings are not coerced to integers, and another enum's case is not accepted just because it has the same backing value. Unknown values throw `TypeMismatchException`. Passing a class that is not a backed enum to the factory throws `ValueException`.

## Dates

`dateTime()` preserves an existing `DateTimeInterface`. An integer timestamp becomes a `DateTimeImmutable` using `@timestamp`; date text is parsed by `DateTimeImmutable`. Relative dates, timezones and date normalization follow PHP's parser. Empty or invalid input throws `TypeMismatchException`.

```php
use Derafu\Value\ValueType;

$date = ValueType::dateTime()->parse('2026-01-15');
$date->format('Y-m-d'); // '2026-01-15'
```

## Custom Types and Error Context

The constructor takes a diagnostic name and a closure receiving the value, path and optional source. Return the typed result or throw `ValueType::mismatch()`:

```php
use Derafu\Value\ValueType;

$even = new ValueType(
    'even',
    static fn (mixed $value, string $path, ?string $source): int
        => is_int($value) && $value % 2 === 0
            ? $value
            : throw ValueType::mismatch($path, 'even', $value, $source),
);

$even->parse(4, 'workers', 'settings.php'); // 4
```

`parse($value, $path = '', $source = null)` attaches a diagnostic location and origin to failures. An empty root path is displayed as `$`. `ValueType::path($parent, $key)` joins a diagnostic path with a string or integer key; it does not navigate data. See [Exceptions and Translations](translations).



---
Last updated on 26/10/2026

