Bei der Erstellung der Bibliothek zur Datenvalidierung quartet
wurden folgende Orientierungspunkte gesetzt:
In diesem Artikel möchte ich mich quartet
auf TypeScript konzentrieren.
Motivation
Wir arbeiten an unseren Projekten mit TypeScript. Als ich diese Bibliothek erstellte, wollte ich, dass die Person, die TypeScript kennt, das Quartett nicht als etwas völlig Neues für ihn lernt , sondern in dieser Bibliothek erkennt, was er bereits weiß.
Benutzerdefinierte Typschutzvorrichtungen
Betrachten Sie ein Beispiel. Wir fordern Daten über den Benutzer mit der API an. Wir gehen davon aus, dass sie vom folgenden Typ sind:
interface User {
id: string;
name: string;
gender: "male" | "female";
age: number;
phoneBook: {
[name: string]: string;
};
}
Was ich von der Validierungsfunktion erhalten möchte:
const probablyUser: unkown = { ... }
if (checkUser(probablyUser)) {
console.log(probablyUser.name)
} else {
throw new Error('Probably User has not type User')
}
Um dieses Ziel zu erreichen, werden benutzerdefinierte Typschutzvorrichtungen verwendet .
Das heißt, die Funktionsdeklaration sollte folgendermaßen aussehen:
function checkUser(probablyUser: any): probablyUser is User {
}
Verwenden wir quartet
, um eine solche Funktion zu erstellen:
import { v } from "quartet";
const checkUser = v({
id: v.string,
name: v.string,
gender: ["male", "female"],
age: v.number
phoneBook: {
[v.rest]: v.string,
}
});
Nachdem wir einen solchen Code geschrieben haben, erhalten wir eine Funktion, die nicht TypeGuard ist:
chechUser: (value: any) => boolean;
Um TypeGuard zu erstellen, muss deklarativ angegeben werden, welcher Typ von dieser Funktion validiert wird. Dies geschieht folgendermaßen:
const checkUser = v<User>({
});
Zusammenfassend:
chechUser: (value: any) => value is User
Zu diesem Artikel gibt es zwei Punkte:
Garantie
Die Tatsache, dass der Entwickler angeben kann, welcher Typ von der Schaltung validiert wird, kann alarmieren, da es durchaus möglich ist, wie folgt zu schreiben:
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 . — .