Lorsque la bibliothèque de validation des données a quartet
été créée, les repères suivants ont été définis:
Dans cet article, je voudrais envisager de me concentrer quartet
sur TypeScript.
Motivation
Nous travaillons sur nos projets en utilisant TypeScript. Par conséquent, lorsque j'ai créé cette bibliothèque, je voulais que la personne qui connaît TypeScript n'apprenne pas le quatuor comme quelque chose de complètement nouveau pour lui, mais reconnaisse dans cette bibliothèque ce qu'il sait déjà.
Protecteurs de type définis par l'utilisateur
Prenons un exemple. Nous demandons des données sur l'utilisateur avec l'API. Nous supposons qu'ils sont du type suivant:
interface User {
id: string;
name: string;
gender: "male" | "female";
age: number;
phoneBook: {
[name: string]: string;
};
}
Ce que je veux obtenir de la fonction de validation:
const probablyUser: unkown = { ... }
if (checkUser(probablyUser)) {
console.log(probablyUser.name)
} else {
throw new Error('Probably User has not type User')
}
Pour atteindre cet objectif, des protections de type définies par l'utilisateur sont utilisées .
Autrement dit, la déclaration de fonction devrait ressembler à ceci:
function checkUser(probablyUser: any): probablyUser is User {
}
Utilisons quartet
pour créer une telle fonction:
import { v } from "quartet";
const checkUser = v({
id: v.string,
name: v.string,
gender: ["male", "female"],
age: v.number
phoneBook: {
[v.rest]: v.string,
}
});
Après avoir écrit un tel code, nous obtenons une fonction qui n'est pas TypeGuard:
chechUser: (value: any) => boolean;
Pour le rendre TypeGuard, il est nécessaire d'indiquer de manière déclarative quel type sera validé par cette fonction. Cela se fait comme ceci:
const checkUser = v<User>({
});
Finalement:
chechUser: (value: any) => value is User
Il y a deux points concernant cet article:
garantie
Le fait même que le développeur puisse indiquer quel type est validé par le circuit peut alerter, car il est tout à fait possible d'écrire comme ceci:
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 . — .