Implémentation de JSON Schema basée sur TypeScript et collection d’outils de développement
(github.com/imhonglu)« Créons une bibliothèque type-safe qui corresponde à mes goûts » : c’est avec cet état d’esprit que ce projet a commencé.
Ce projet a démarré avec une implémentation type-safe de JSON Schema, puis s’est naturellement étendu à divers outils nécessaires au processus de développement.
J’y ai pour l’instant mis un premier point d’arrêt dans le cadre de ma recherche d’emploi.
Principes du projet
Le développement a été mené en respectant les principes fondamentaux suivants :
- utilisation d’un système de types strict
- maintien d’un minimum de dépendances externes
- conception d’un système de types réutilisable
- documentation de l’API
- maintien d’une couverture de tests élevée
- implémentation en TypeScript pur
Bibliothèques
@imhonglu/json-schema
Implémentation TypeScript conforme à la spécification JSON Schema draft 2020-12.
- Dépôt : https://github.com/imhonglu/new-wheels/…
- Validation via
JSON-Schema-Test-Suite - Les types des mots-clés disponibles sont automatiquement inférés selon la définition du schéma.
import { Schema, SchemaDefinition } from "@imhonglu/json-schema";
export const Address = new Schema({
type: "object",
properties: {
street: { type: "string" },
city: { type: "string" },
zip: { type: "string" },
},
required: ["street"] as const,
});
export type Address = SchemaDefinition.Instance<typeof Address>;
// {
// street: string;
// city?: string;
// zip?: string;
// }
@imhonglu/format
Projet lancé pour implémenter le mot-clé format de JSON Schema.
- Dépôt : https://github.com/imhonglu/new-wheels/…
- Implémentation basée sur les spécifications RFC
- Validation basée sur
JSON-Schema-Test-Suite - Fournit une interface similaire à l’API native
JSON
import { FullTime } from '@imhonglu/format';
const time = FullTime.parse('00:00:00.000Z');
// { hour: 0, minute: 0, second: 0, secfrac: '.000', offset: undefined }
console.log(time.toString()); // '00:00:00.000Z'
console.log(JSON.stringify(time)); // '"00:00:00.000Z"'
const result = FullTime.safeParse('invalid');
if (!result.ok) {
console.error(result.error);
}
@imhonglu/pattern-builder
Un builder regex créé pour améliorer la lisibilité des expressions régulières lors de l’implémentation de la grammaire ABNF des spécifications RFC.
import { characterSet, concat, hexDigit } from "@imhonglu/pattern-builder";
// pct-encoded = "%" HEXDIG HEXDIG
export const pctEncoded = concat(
"%",
hexDigit.clone().exact(2),
);
// unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
export const unreserved = characterSet(
alpha,
digit,
/[\-._~]/,
);
@imhonglu/type-guard
Bibliothèque de type guards créée pour améliorer la lisibilité des garde-types.
- Dépôt : https://github.com/imhonglu/new-wheels/…
- Implémentation basée sur Proxy pour minimiser l’overhead
- Fournit le mot-clé
not
import { composeGuards } from "@imhonglu/type-guard";
const is = composeGuards({
string: (value: unknown): value is string => typeof value === "string",
number: (value: unknown): value is number => typeof value === "number"
});
is.string("hello"); // true
is.not.string(42); // true
let value: string | number | undefined;
if (is.number(value)) {
value.toFixed(2); // 'value' is number
}
if (is.not.number(value)) {
value.toFixed(2); // error: Property 'toFixed' does not exist on type 'undefined'.
}
@imhonglu/type-object
Bibliothèque wrapper type-safe de l’API native Object. Elle fournit des types proches du comportement natif.
import * as TypeObject from '@imhonglu/type-object';
const data = { a: 1, b: 2, c: 3 };
for (const key of TypeObject.keys(data)) {
// key: "a" | "b" | "c"
console.log(data[key]); // number
}
const string = 'hello';
for (const index of TypeObject.keys(string)) {
// index: number & keyof string
console.log(string[index]); // string
}
@imhonglu/toolkit
Collection de types utilitaires et de fonctions utilitaires utilisés en interne dans le projet.
import type { Fn } from '@imhonglu/toolkit';
// Fournit un alias de type pour le type de fonction '(...args: any[]) => any'.
Fn.Callable // (...args: any[]) => any
// Via les génériques, vous pouvez définir uniquement le type des arguments.
Fn.Callable<{ args: [number, number] }> // (...args: [number, number]) => any
// Via les génériques, vous pouvez définir uniquement le type de retour.
Fn.Callable<{ return: string }> // (...args: any[]) => string
// Via les génériques, vous pouvez définir à la fois le type des arguments et le type de retour.
Fn.Callable<{ args: [number, number], return: string }> // (...args: [number, number]) => string
Projets futurs et recherche d’emploi
La prochaine étape de ce projet en cours est de finaliser l’implémentation de la spécification JSON Schema,
puis j’aimerais écrire un framework backend.
Je suis actuellement en recherche d’emploi, donc tout intérêt porté à mon travail est le bienvenu.
Merci de m’avoir lu.
Je vous souhaite une excellente journée !
2 commentaires
Dans ce domaine, il y a l’excellent zod, donc on l’utilise pour le produit, mais c’est intéressant.
Les projets existants comme ajv, typia et zod sont aussi des projets que je suis avec beaucoup d’intérêt.
Dans le cas de
safeParsede@imhonglu/format, c’est aussi une fonctionnalité influencée par l’API de zod.Merci pour votre intérêt !