# reflect-types

> Type combinators for JavaScript/TypeScript at runtime

Latest version **5.1.3** (published 2026-04-16) · MIT license · 0 weekly downloads

## Install

```sh
npm install reflect-types
pnpm add reflect-types
yarn add reflect-types
bun add reflect-types
```

## Health

**Score 60/100 (C)** — status: active.

Positive: has types; esm support; no vulnerabilities; high quality score.

Warnings: low downloads.

## Facts

| | |
|---|---|
| Version | 5.1.3 |
| Published | 2026-04-16 |
| First published | 2017-11-30 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Dependencies | 0 |
| Unpacked size | 165.3 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| GitHub stars | 0 |
| Author | Sam Vervaeck |
| Maintainers | samvv |
| Keywords | type, validation, schema, specification, runtime |

## Links

- npm: https://www.npmjs.com/package/reflect-types
- Repository: https://github.com/samvv/reflect-types
- Homepage: https://github.com/samvv/reflect-types#readme
- Issues: https://github.com/samvv/reflect-types/issues
- npm.io page: https://npm.io/package/reflect-types

## Alternatives

- [@regle/core](https://npm.io/package/@regle/core.md) — 47.0K weekly downloads
- [typeof-arguments](https://npm.io/package/typeof-arguments.md) — 12.5K weekly downloads
- [@lokalise/projects-engine-contracts](https://npm.io/package/@lokalise/projects-engine-contracts.md) — 978 weekly downloads
- [@osjwnpm/nam-laboriosam-quibusdam](https://npm.io/package/@osjwnpm/nam-laboriosam-quibusdam.md) — 70 weekly downloads
- [@oridune/validator](https://npm.io/package/@oridune/validator.md) — 16 weekly downloads

## Recent versions

- 5.1.3 (latest) — 2026-04-16
- 5.1.2 — 2026-04-16
- 5.1.1 — 2026-04-16
- 5.1.0 — 2026-03-19
- 5.0.0 — 2026-03-19
- 4.1.0 — 2026-03-19
- 4.0.0 — 2026-03-17
- 3.1.3 — 2026-03-17
- 3.1.2 — 2026-03-17
- 3.1.1 — 2026-03-17
- 3.1.0 — 2026-03-15
- 3.0.0 — 2026-03-14
- 2.4.1 — 2026-03-14
- 2.4.0 — 2026-03-14
- 2.3.1 — 2026-03-13
- … 7 more at https://npm.io/package/reflect-types/versions

## README

Reflect Types
=============

Reflect types allows you to work with types both at compile time and at runtime. 

Think [Zod][zod], but with the useful feature of being able to analyse and traverse the types, not just use them for validation.

[zod]: https://www.npmjs.com/package/zod

Why would I want to use this?

 - You want to declare and validate some JavaScript objects at the same time
 - You want to do some metaprogramming like in C++
 - You want to extend the system with your own types easily and safely
 - You want your users to specify types of data and be able to inspect what they return

## Examples

**Define an object type and validate some data with it**
```ts
import t, { type Infer, validate } from "reflect-types"

const personT = t.object({
    id: t.uuid4(),
    fullName: t.string(),
    email: t.email(),
    dateOfBirth: t.date(),
});

type Person = Infer<typeof personT>;

const person1: Person = {
    id: '22ba434a-c662-4cc9-8a05-5cf1c7c90fd7',
    fullName: 'James Smith',
    email: 'james.smith@gmail.com',
    dateOfBirth: new Date('8/23/1997'),
}

const [errors, result] = validate(person1, personT);

if (errors.length > 0) {
    for (const error of errors) {
        console.log(error);
    }
} else {
    // No errors, yay! Now, `result` may be stored in the database.
}
```

**Inspect a type in order to infer whether it is nullable**

This is where this library really shines.

```ts
import { type Type } from "reflect-types"

function isNullable(type: Type): boolean {
    switch (type.kind) {
        case 'literal':
            return type.value === null;
        case 'null':
            return true;
        case 'union':
            return (type.types as Type[]).some(isNullable);
        default:
            return false;
    }
}

// @ts-expect-error
function fetchSomeTypeSomehow(): Type {
    // TODO
}

const type = fetchSomeTypeSomehow();

console.log(
  isNullable(type)
    ? `The computed type is nullable.`
    : `The computed type is not nullable.`);
```

## Installation

Just install `reflect-types` using your favorite package manager, e.g.:

```sh
npm install reflect-types
```

## API Reference

Most examples in this section require the following import to be present:

```ts
import t from "reflect-types";
```

### `Infer<T>`

Infer the value of a certain reflected type.

Use the `typeof` keyword to lift the reflected type to the TypeScript type
level.

**Example:**

```ts
import t, { type Infer } from "reflect-types";

const objT = t.object({
    foo: t.number(),
    bar: t.string(),
});

type Obj = Infer<typeof objT>;

const fo = {
    foo: 1,
    bar: "hello",
} satisfies Obj;
```

### Primitive Types

#### `t.undefined()`

Represents the TypeScript `undefined` type.

#### `t.null()`

Represents the TypeScript `null` type.

#### `t.boolean()`

Represents the TypeScript `boolean` type.

#### `t.number()`

Represents the TypeScript `number` type.

#### `t.string()`

Represents the TypeScript `string` type.

#### `t.literal(value)`

Represents a TypeScript literal type.

**Examples:**

```ts
import t, { type Infer } from "reflect-types";

const trueT = t.literal(true);
const foobarT = t.literal("foobar");
const theAnswerT = t.literal(42);

const x1: Infer<typeof trueT> = true; // ok
// @ts-expect-error
const x2: Infer<typeof trueT> = false; // type error
const x3: Infer<typeof foobarT> = "foobar"; // ok
// @ts-expect-error
const x4: Infer<typeof foobarT> = "blablabla"; // type error
const x5: Infer<typeof theAnswerT> = 42; // ok
// @ts-expect-error
const x6: Infer<typeof theAnswerT> = 3; // type error
```

#### `t.date()`

Represents a plain `Date` object in JavaScript.

### Container Types

#### `t.array(elementType)`

Represents a TypeScript `Array<T>` where `T` is the inferred type of
`elementType`.

```ts
import t from "reflect-types";

const numbersT = t.array(t.number());

const personsT = t.array(t.object({
    fullName: t.string(),
    dateOfBirth: t.date(),
    email: t.email(),
}));
```

#### `t.object(objLiteral)`

Represents an object literal at the type level, where the keys and values are
specified in `objLiteral`.

```ts
import t from "reflect-types";

const loginT = t.object({
    username: t.string(),
    password: t.string(),
    rememberMe: t.boolean(),
});
```

#### `t.optional(inner)`

> [!WARNING]
>
> This constructor must only be used inside `t.object()`.

Marks a field of the object being defined as optional.

`inner` is a reflected type that represents the type of the field.

```ts
import t, { type Infer } from "reflect-types";

const productT = t.object({
    title: t.string(),
    description: t.optional(t.string()),
    price: t.string(),
});

type Product = Infer<typeof productT>;

// Notice the 'description' field is omitted
const beans = {
    title: "Can of Beans",
    price: "3.00",
} satisfies Product;
```

### Special Types

#### `t.any()`

Resents the TypeScript `any` type.

This type accepts any value given to it.

#### `t.union(elementTypes)`

Represents a TypeScript union type.

`elementTypes` must be an array of reflected types, like so:

```ts
import t from "reflect-types";

const stringOrNumberT = t.union([
    t.string(),
    t.number(),
]);
```

### Helper Types

#### `t.nullable(inner)`

Represents a union of the type of `inner` and a null literal type.

```ts
import t from "reflect-types";

const maybeStringT = t.nullable(t.string());
```

### Functions and Execution

#### `t.callable(params, returns)`

Represents a function signature.

Note that due to a limitation in TypeScript you need to add `as const` to the
parameter type array, like so:

```ts
import t, { type Infer } from "reflect-types";

const stringLengthSig = t.callable(
    [ t.string() ] as const,
    t.number()
);

const getLength: Infer<typeof stringLengthSig> = x => x.length;
```

#### `t.void()`

Represents the TypeScript `void` type.

The TypeScript `void` type is similar to `undefined`, but is not aways
unifiable with it. In order to correctly declare and define functions that
return nothing, you may need this type.

#### `t.promise(awaitedType)`

Represents the `Promise<T>` TypeScript type, where `T` is represented by `awaitedType`.

```ts
import t, { type Infer } from "reflect-types";

const methodT = t.callable(
    [ t.string() ] as const,
    t.promise(t.void()),
);
```

### Type API

#### `Type.kind`

A unique ID for the category that the given type defines.

For instance, any `ArrayType` always has its `kind` set to `array`.

This field can be used to automatically cast to the associated type, like so:

```ts
import type { Type } from "reflect-types";

function analyse(ty: Type) {
    if (ty.kind === 'array') {
        // Note that we didn't need to cast to use `.elementType`
        console.log(ty.elementType);
    } else if (ty.kind === 'object') {
        // Note that we didn't need to cast to use `.entries`
        console.log(ty.entries);
    } else {
        // ...
    }
}
```

#### `Type.__type`

This field holds the TypeScript type that is associated with the given
reflection type.

You generally don't access it directly. Instead, you use `Infer<T>` to
calculate the TypeScript type for you.

While it is discouraged to access this field, you need to _define_ this field
when you want to add a reflection type. See _advanced usage_ for more
information.

## Advanced Usage

### Fixing compile errors while inspecting reflection types

Due to an open issue in TypeScript, we are currently not able to let generics
extend from `Type`. Instead, we have to extend them from `TypeBase`.

If you're just an end-user, this won't pose any problems. However, if you're
inspecting a reflection type in your application then you might run into errors
like these:

```
error TS2345: Argument of type 'TypeBase' is not assignable to parameter of type 'Type'.
```

The fix is (unfortunately) to add a `as Type` after accessing one of the
members of a reflection type.

```ts
import type { Type } from "reflect-types";

function analyse(ty: Type) {
    if (ty.kind === 'array') {
        analyse(ty.elementType as Type); // `as Type` fixes the compiler error
    } else {
        // ...
    }
}
```

You could even wrap this logic in a function:

```ts
import type { TypeBase, Type } from "reflect-types"

function asType(value: TypeBase): Type {
    return value as Type;
}
```

With this function you only need to update one location should we ever be
forced to make changes to how this quirk is handled.

### Extending the type system

The type system is flexible enough to be extended with user-defined types.

Here is a code snippet that create a new type for RGB-colors.

```ts
import type { TypeBase } from "reflect-types"

type RGB = [r: number, g: number, b: number];

export class RGBType implements TypeBase {

    // Give your type an unique name. Names should be lowercase and use dashes
    // when consisting of multiple words.
    readonly kind = 'rgb';

    // This resolves to the actual TypeScript type of value that is held by this type.
    __type!: RGB;

}

declare module "reflect-types" {
    interface Types {
        rgb: RGBType,
    }
}

export function rgb(): RGBType {
    return new RGBType();
}
```

In the code above, we first define a TypeScript type for the values we wish to support, in this case `RGB`.

Next, we create the actual class that will represent these values in
`reflect-types`. Usually these have the suffix `Type`. They implement
`TypeBase`, a minimal interface that every type should adhere to.

The fields `kind` and `__type` indicate the tag and the TypeScript type,
respectively. Next, the `declare module`-directive ensures that when a user
specifies our new type somewhere, it is actually accepted by e.g.
`t.object()`.

Finally, we create a simple constructor for our type, making the class
transparent and avoiding the use of `new`.

## License

This project is licensed under the MIT license. See `LICENSE.txt` for more information.

---
_Source: https://npm.io/package/reflect-types · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
