Runtypes

Runtime validation for static types

README

Runtypes


Safely bring untyped data into the fold


Runtypes allow you to take values about which you have no assurances and check that they conform to some type A. This is done by means of composable type validators of primitives, literals, arrays, tuples, records, unions, intersections and more.

Installation


  1. ```
  2. npm install --save runtypes
  3. ```

Example


Suppose you have objects which represent asteroids, planets, ships and crew members. In TypeScript, you might write their types like so:

  1. ```ts
  2. type Vector = [number, number, number]

  3. type Asteroid = {
  4. type: "asteroid"
  5. location: Vector
  6. mass: number
  7. }

  8. type Planet = {
  9. type: "planet"
  10. location: Vector
  11. mass: number
  12. population: number
  13. habitable: boolean
  14. }

  15. type Rank = "captain" | "first mate" | "officer" | "ensign"

  16. type CrewMember = {
  17. name: string
  18. age: number
  19. rank: Rank
  20. home: Planet
  21. }

  22. type Ship = {
  23. type: "ship"
  24. location: Vector
  25. mass: number
  26. name: string
  27. crew: CrewMember[]
  28. }

  29. type SpaceObject = Asteroid | Planet | Ship
  30. ```

If the objects which are supposed to have these shapes are loaded from some external source, perhaps a JSON file, we need to validate that the objects conform to their specifications. We do so by building corresponding Runtypes in a very straightforward manner:

  1. ```ts
  2. import { Boolean, Number, String, Literal, Array, Tuple, Record, Union } from "runtypes"

  3. const Vector = Tuple(Number, Number, Number)

  4. const Asteroid = Record({
  5. type: Literal("asteroid"),
  6. location: Vector,
  7. mass: Number,
  8. })

  9. const Planet = Record({
  10. type: Literal("planet"),
  11. location: Vector,
  12. mass: Number,
  13. population: Number,
  14. habitable: Boolean,
  15. })

  16. const Rank = Union(Literal("captain"), Literal("first mate"), Literal("officer"), Literal("ensign"))

  17. const CrewMember = Record({
  18. name: String,
  19. age: Number,
  20. rank: Rank,
  21. home: Planet,
  22. })

  23. const Ship = Record({
  24. type: Literal("ship"),
  25. location: Vector,
  26. mass: Number,
  27. name: String,
  28. crew: Array(CrewMember),
  29. })

  30. const SpaceObject = Union(Asteroid, Planet, Ship)
  31. ```

(See the examples directory for an expanded version of this.)

Now if we are given a putative SpaceObject we can validate it like so:

  1. ```ts
  2. // spaceObject: SpaceObject
  3. const spaceObject = SpaceObject.check(obj)
  4. ```

If the object doesn't conform to the type specification, check will throw an exception.

Error information


When it fails to validate, your runtype emits a ValidationError object that contains detailed information that describes what's the problem. Following properties are available in the object:

- name: Always "ValidationError"
- message: A string that summarizes the problem overall
- code: A [Failcode](https://github.com/pelotom/runtypes/blob/dcd4fe0d0bd0fc9c3ec445bda30586f3e6acc71c/src/result.ts#L12-L33) that categorizes the problem
- details: An object that describes which property was invalid precisely; only for complex runtypes (e.g. Record, Array, and the like)

If you want to inform your users about the validation error, it's strongly discouraged to rely on the format of message property in your code, as it may change across minor versions for readability thoughts. Instead of parsing message, you should use code and/or details property to programmatically inspect the validation error, and handle other stuff such as i18n.