typist-json
v1.0.1
Published
A simple runtime JSON type checker
Downloads
23
Readme
Features
- Simple. No JSON Schema, No validation rules
- Type-safe. Written in TypeScript
- Intuitive. Familiar syntax like TypeScript interface
Typist JSON is focused on type checking, so there is no validation rules like range of numbers or length of strings.
Install
npm install typist-json
NOTE: Require TypeScript 4.1 or higher because Typist JSON uses Key Remapping
and Template Literal Types
.
Example
import { j } from "typist-json";
const NameJson = j.object({
firstname: j.string,
lastname: j.string,
});
const UserJson = j.object({
name: NameJson,
age: j.number,
"nickname?": j.string, // optional property
});
const userJson = await fetch("/api/user")
.then(res => res.json());
if (UserJson.check(userJson)) {
// now, the userJson is narrowed to:
// {
// name: {
// firstname: string
// lastname: string
// }
// age: number
// nickname?: string | undefined
// }
}
Circular References
Sometimes JSON structures can form circular references.
Typist JSON can represent circular references by wrapping checkers in the arrow function.
const FileJson = j.object({
filename: j.string,
});
const DirJson = j.object({
dirname: j.string,
entries: () => j.array(j.any([FileJson, DirJson])), // references itself
});
DirJson.check({
dirname: "animals",
entries: [
{
dirname: "cat",
entries: [
{ filename: "american-shorthair.jpg" },
{ filename: "munchkin.jpg" },
{ filename: "persian.jpg" },
],
},
{
dirname: "dog",
entries: [
{ filename: "chihuahua.jpg" },
{ filename: "pug.jpg" },
{ filename: "shepherd.jpg" },
],
},
{ filename: "turtle.jpg" },
{ filename: "shark.jpg" },
],
}); // true
Type checkers
Strings
j.string.check("foo"); // true
j.string.check("bar"); // true
Numbers
j.number.check(42); // true
j.number.check(12.3); // true
j.number.check("100"); // false
Booleans
j.boolean.check(true); // true
j.boolean.check(false); // true
Literals
j.literal("foo").check("foo"); // true
j.literal("foo").check("fooooo"); // false
Arrays
j.array(j.string).check(["foo", "bar"]); // true
j.array(j.string).check(["foo", 42]); // false
j.array(j.string).check([]); // true
j.array(j.number).check([]); // true
Objects
j.object({
name: j.string,
age: j.number,
"nickname?": j.string,
}).check({
name: "John",
age: 20,
nickname: "Johnny",
}); // true
j.object({
name: j.string,
age: j.number,
"nickname?": j.string,
}).check({
name: "Emma",
age: 20,
}); // true, since "nickname" is optional
j.object({
name: j.string,
age: j.number,
"nickname?": j.string,
}).check({
id: "xxxx",
type: "android",
}); // false, since "name" and "age" is required
If a property that ends with ?
is not optional, you should replace all trailing ?
by ??
.
As mentioned above, you need to escape all trailing ?
as ??
.
j.object({
"foo??": j.boolean,
}).check({
"foo?": true,
}); // true
So if you want optional property with a name "foo???"
,
you should use "foo???????"
as key.
j.object({
"foo???????": j.boolean,
}).check({}); // true, since "foo???" is optional
Nulls
j.nil.check(null); // true
j.nil.check(undefined); // false
Nullables
j.nullable(j.string).check("foo"); // true
j.nullable(j.string).check(null); // true
j.nullable(j.string).check(undefined); // false
Unknowns
j.unknown.check("foo"); // true
j.unknown.check(123); // true
j.unknown.check(null); // true
j.unknown.check(undefined); // true
j.unknown.check([{}, 123, false, "foo"]); // true
Unions
j.any([j.string, j.boolean]).check(false); // true
j.any([
j.literal("foo"),
j.literal("bar"),
]).check("foo"); // true
Get JSON type of checkers
import { j, JsonTypeOf } from "typist-json";
const UserJson = j.object({
name: j.string,
age: j.number,
"nickname?": j.string,
});
type UserJsonType = JsonTypeOf<typeof UserJson>;
//
// ^ This is same as:
//
// type UserJsonType = {
// name: string;
// age: number;
// nickname?: string;
// }