@jakobkg/shapes-ts
v0.4.0
Published
Toy library to create runtime safe types in TS.
Readme
shapes-ts
Toy library to create runtime safe types in TS.
When describing the shape of your data in shapes-ts, you get the corresponding TS type and a validation utility for free!
const CreditCategory = Shapes.enum(["director", "writer", "actor/actress"]);
const Credits = Shapes.object({
category: CreditCategory,
name: Shapes.string(),
});
const Movie = Shapes.object({
title: Shapes.string(),
rating: Shapes.number().where(
(n) => n >= 0 && n <= 5,
"rating must be in range 0.0-5.0",
),
credits: Shapes.array(Credits),
});
// This infers as
// {
// title: string,
// rating: number,
// credits: {
// category: "director" | "writer" | "actor/actress",
// name: string
// }[]
// }
type Movie = Shapes.Type<typeof Movie>;
const movieData: unknown = getFromIMDB("tt23289160");
if (!Movie.check(movieData)) {
throw new Error("Unexpected data from API");
}
// Past this point, movieData is typed as Movie
for (const credit of movieData.credits) {
console.log(`${credit.name}: ${credit.category}`);
}Supported types
Primitives
- string
- number
- boolean
...and literals of these
Composite types
- object
- array
- tuple
- enum
Modified types
- Nullable types (
T | null) - Optional types (
T | undefined) - Unions (
A | B) - Intersections (
A & B)
Predicates
Shapes can have additional validation checks added to them. These do not affect
the inferred type, but are still ran as part of a shape's .check() method to
help enforce constraints that are not easily modeled using the type system.
const Username = Shapes.string().where(
(str) => str.length < 10,
"must be less than 10 characters long",
);
Username.check("jakob :)"); // true
Username.check("CptAmericaFan207"); // falsePredicates can be used with all types of shapes, and they may be combined both by stacking them on a single shape and by combining shapes that already have predicates on them.
const Fizz = Shapes.number().where(
(n) => n % 3 === 0,
"must be divisible by 3",
);
const Buzz = Shapes.number().where(
(n) => n % 5 === 0,
"must be divisible by 5",
);
const FizzBuzz = Fizz.and(Buzz);
Fizz.check(3); // true
FizzBuzz.check(3); // false
Fizz.check(5); // false
Buzz.check(5); // true
FizzBuzz.check(15); //true