When the library for data validation quartet
was created, the following landmarks were set:
In this article, I would like to consider focusing quartet
on TypeScript.
Motivation
We work on our projects using TypeScript. Therefore, when I created this library, I wanted the person who knows TypeScript to not learn quartet as something completely new to him, but to recognize in this library what he already knows.
User-Defined Type Guards
Consider an example. We request data about the user with the API. We assume that they are of the following type:
interface User {
id: string;
name: string;
gender: "male" | "female";
age: number;
phoneBook: {
[name: string]: string;
};
}
What I want to get from the validation function:
const probablyUser: unkown = { ... }
if (checkUser(probablyUser)) {
console.log(probablyUser.name)
} else {
throw new Error('Probably User has not type User')
}
To achieve this goal, User-Defined Type Guards are used .
That is, the function declaration should look like this:
function checkUser(probablyUser: any): probablyUser is User {
}
Let's use quartet
to create such a function:
import { v } from "quartet";
const checkUser = v({
id: v.string,
name: v.string,
gender: ["male", "female"],
age: v.number
phoneBook: {
[v.rest]: v.string,
}
});
Having written such code, we get a function that is not TypeGuard:
chechUser: (value: any) => boolean;
To make it TypeGuard it is necessary to declaratively indicate which type will be validated by this function. This is done like this:
const checkUser = v<User>({
});
Eventually:
chechUser: (value: any) => value is User
There are two points regarding this item:
Warranty
The very fact that the developer can indicate which type is validated by the circuit can alert, because it is quite possible to write like this:
const checkNumber = v<number>({ name: v.string });
— { name: string }
, , number
.
. ( ) , — "" .
, .
, v
. :
const v: <T>(schema: Schema) => (value: any) => value is T;
.
, TypeGuard .
:
const v: <T = any>(schema: Schema) => (value: any) => value is T;
, — never
:
const checkNumber = v(v.number);
const value: any = "123";
if (!checkNumber(value)) {
}
,
,
.
, T
any
, T === any
, .
- :
const v: <T = any>(
schema: Schema
) => IfAny<T, (value: any) => boolean, (value: any) => value is T>;
type IfAny<T,A,B> =
— , :
T — any
:
type IfAny<T, A, B> = true extends T
? "1" extends T
? 1 extends T
? {} extends T
? (() => void) extends T
? null extends T
? A
: B
: B
: B
: B
: B
: B;
, , : boolean | number | string | object | function | null
any
.
TypeScript
, , TypeScript'a .
@hapi/joi
ajv
, User
.
Text Compare .
quartet
const checkUser = v({
id: v.string,
name: v.string,
gender: ["male", "female"],
age: v.number
phoneBook: {
[v.rest]: v.string,
}
})

- : 24
const schema = j.object({
id: j.string().required(),
name: j.string().required(),
gender: j
.string()
.valid("male", "female")
.required(),
age: j.number().required(),
phoneBook: j.object().pattern(/.*/, j.string())
});

, , 118 .
ajv
const checkUser = a.compile({
type: "object",
required: ["id", "name", "gender", "age", "phoneBook"],
properties: {
id: { type: "string" },
name: { type: "string" },
gender: { type: "string", enum: ["male", "female"] },
phoneBook: {
type: "object",
additionalProperties: {
type: "string"
}
}
}
});

, 146 .
:

TypeScript. , , , .
TypeGuard — , .
TypeScript . — .