Type safety

at runtime

in typescript

@kajetansw

kajetan.dev

Kajetan Świątek

👨‍🎨 Front-end developer

🇵🇱 Wrocław

typescript

is not perfect

(not every tool will meet all expectations of all users)

runtime type system:

The missing piece?

underneath, typescript is

plain javascript

No interfaces

and type aliases

No runtime validation

after compilation

interface Hero {
  name: string;
  gender: string;
}

const url = 'https://swapi.dev/api/people/1';
const hero: Hero = 
  await fetch(url).then(r => r.json());

// How sure are we that the returned object is
// really of type `Hero`?

type assertion of runtime values

what's

the solution?

(Runtime validators, schema validators, json decoders)

Runtime types

elm

what abouT        ?

what abouT        ?

benchmarks (OPS/sec)

typescript-runtime-type-benchmarks

what abouT        ?

jquense/yup
⭐️ 16,787

ajv-validator/ajv
⭐️ 10,921

colinhacks/zod
⭐️ 7,118

gcanti/io-ts
⭐️ 5,366

pelotom/runtypes
⭐️ 2,074

library by github stars

PREVIEW

zod

zod - schema primitives 

import { z } from "zod";

// primitive values
z.string();
z.number();
z.bigint();
z.boolean();
z.date();

// empty types
z.undefined();
z.null();

zod - being more specific 

// strings
z.string().max(5);
z.string().min(5);
z.string().length(5);
z.string().email();
z.string().url();
// etc.
// numbers
z.number().gt(5);
z.number().gte(5);
z.number().lt(5);
z.number().lte(5);
z.number().int();
// etc.

zod - complex data

const Dog = z.object({
  name: z.string(),
  age: z.number(),
});

// extract the inferred type
type Dog = z.infer<typeof Dog>;

// equivalent to:
type Dog = {
  name: string;
  age: number;
};

Also combinators for 

- extending

- merging

- picking

- omitting

zod - usage

// creating a schema for strings
const mySchema = z.string();

// parsing
mySchema.parse("tuna"); // "tuna"
mySchema.parse(12); 	// throws ZodError

// doesn't throw error if validation fails
mySchema.safeParse("tuna"); 
// { success: true; data: "tuna" }
mySchema.safeParse(12); 
// { success: false; error: ZodError }

io-ts (from fp-ts ecosystem)

import * as t from 'io-ts'

// Primitives
t.string
t.number

// Combinators
const User = t.type({
  userId: t.number,
  name: t.string
})

type User = t.TypeOf<typeof User>
import * as t from 'io-ts'

// Decoding
t.string.decode(/*...*/) 
// returns Either<E, A>
                     
type Either<E, A> =
  | {
      readonly _tag: 'Left'
      readonly left: E
    }
  | {
      readonly _tag: 'Right'
      readonly right: A
    }

your turn!

TRY THEM OUT!

Thank you!

✋😎

@kajetansw

kajetan.dev

typescript-runtime-type-benchmarks

Made with Slides.com