Javascript : Comment valider une instance de classe ES6 au runtime

Quand le Typescript ne suffit plus !

Javascript : Comment valider une instance de classe ES6 au runtime

L'intĂ©gritĂ© des donnĂ©es est trĂšs importante en Javascript, le langage Ă©tant dynamiquement typĂ©, une donnĂ©e n'ayant pas le bon type ou Ă©tant absente peut causer l'arrĂȘt du programme, ou pire, le programme s'exĂ©cutera mais pas de la maniĂšre prĂ©vue.

Et là, je sais ce que vous pensez : "Mais c'est pour ça qu'on utilise Typescript !"

Et je vous répondrai :

Oui, mais cela ne résout pas tous les problÚmes, loin de là.

Si vous voulez comprendre en détails pourquoi, je vous invite à lire mon article d'introduction à TypeScript.

Mais en rĂ©sumĂ©, Typescript permet "simplement" d'Ă©viter des erreurs de typage lors de la phase de dĂ©veloppement du programme, mais pour toutes les donnĂ©es qui n'existe dans le programme que lors de l'exĂ©cution, comme par exemple les imports de fichiers (csv,...), le localStorage ou encore le contenu de requĂȘtes http, alors TypeScript ne suffit plus !

Pour plus d'informations, je vous invite Ă  lire cet article sur le blog d'Oreilly.

typestack/class-validator

AprÚs quelques recherches et plusieurs tests, j'ai finalement trouvé la bibliothÚque "class-validator" qui permet tout simplement de valider le contenu d'un objet, instancié depuis une classe.

typestack/class-validator
Decorator-based property validation for classes. Contribute to typestack/class-validator development by creating an account on GitHub.
Pour ceux qui se poseraient la question sur la fiabilité de cette bibliothÚque, "class-validator" est utilisé en interne par NestJS pour son mécanisme de DTO.

Pour l'utiliser il vous suffira de faire un "npm install --save class-validator"

Le fonctionnement

Pour préparer une classe à la validation, "class-validator" fournie un ensemble de décorateurs à ajouter au-dessus de chaque attribut de la classe, comme dans l'exemple suivant :

A noter que tous les exemples de cet article sont Ă©crits en Typescript, bien Ă©videmment la bibliothĂšque peut ĂȘtre importĂ©e en Javascript classique.
//index.ts
import { IsString, IsInt, IsOptional, validate } from 'class-validator';

class Person {
    
    @IsString()
    firstname: string

    @IsString()
    lastname:string

    @IsInt()
    @IsOptional()
    age: number

    constructor(csv_arr: string[]){
        this.firstname = csv_arr[0];
        this.lastname = csv_arr[1];
        this.age = csv_arr[2] ? parseInt(csv_arr[2]) : undefined;
    }
}

Ici j'ai pris l'exemple d'un fichier csv que l'on importerait, on initialise donc le constructeur avec un simple tableau de chaĂźnes de caractĂšres (une ligne du fichier .csv).

Ensuite, il va nous suffire d'instancier toutes nos personnes à partir des données issues du fichier et d'appeler la méthode de validation.

A noter que pour simplifier l'exemple, le fichier a été remplacé par un tableau statique, si vous voulez savoir comment importer un fichier csv, j'ai écrit un article détaillé sur le sujet.
//index.ts
let csv : any[][] = [
    ["Marc", "Devier", 40],
    ["Alice", "Boran",],
    ["Jean",,52]
]

let persons: Person[] = []; // Contains only valid persons

for(let i:number = 0 ; i<csv.length ; i++){

    let person: Person = new Person(csv[i]);
    
    validate(person).then(errors => {
        if(errors.length === 0){
            persons.push(person);
            console.log(person);
        } else {
            console.error(errors);
        }
    });

}

Lors de l'exécution de ce programme, notre tableau "persons" ne contiendra donc que des instances de "Person" valides, l'intégrité des données est donc respectée.

Voici donc les informations loggées lors du déroulement du programme :

Person { firstname: 'Marc', lastname: 'Devier', age: 40 }
Person { firstname: 'Alice', lastname: 'Boran', age: undefined }
[
  ValidationError {
    target: Person { firstname: 'Jean', lastname: undefined, age: 52 },
    value: undefined,
    property: 'lastname',
    children: [],
    constraints: { isString: 'lastname must be a string' }
  }
]

On y voit bien que notre deuxiĂšme objet a Ă©tĂ© validĂ©, mĂȘme si son age n'est pas dĂ©fini car il a Ă©tĂ© dĂ©clarĂ© comme optionnel, mais qu'au contraire notre troisiĂšme objet n'est pas valide car il manque son nom de famille.

Il existe énormément de décorateurs différents, non pas simplement sur le type de donnée mais aussi le contenu de cette derniÚre (on peut valider la taille d'une string, ou encore vérifier que c'est une adresse email par exemple).

Pour plus d'informations je vous invite Ă  consulter la documentation directement sur Github : https://github.com/typestack/class-validator

Alternative

Bien sûr il existe d'autres alternatives, comme JOI par exemple, qui vous permettra non plus de valider une instance de classe, mais n'importe quel objet JSON !

J'espĂšre que cet article vous aura plus, et Ă  bientĂŽt sur le blog !

Les articles les plus populaires du blog

Envie de continuer à lire des articles autour du développement web (entre autres) ? Voici la sélection des articles de mon blog les plus lus par la communauté !

Voir la sĂ©lection 🚀

Recevez les articles de la semaine par e-mail pour ne rien manquer !

S'abonner à la newsletter 📧
Mes formations disponibles 🎓  -5% inclus pour les lecteurs du blog

À propos de l'auteur

Hello, je suis Nicolas Brondin-Bernard, ingénieur web indépendant depuis 2015 passionné par le partage d'expériences et de connaissances.

Aujourd'hui je suis aussi formateur/coach pour dĂ©veloppeurs web juniors, tu peux me contacter sur nicolas@brondin.com, sur mon site ou devenir membre de ma newsletter pour ne jamais louper le meilleur article de la semaine et ĂȘtre tenu au courant de mes projets !