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à.

Je m'explique :

Typescript est un langage (en fait c'est un sur-ensemble, ou "superset") au-dessus de Javascript qui permet d'ajouter des types statiques stricts à ce dernier lors de la phase de développement. Une fois qu'un fichier Typescript aura passé la phase de validation et aura été transpilé en Javascript, le fait que le programme aura été développé en TS n'aura plus aucun effet, ni sur la validation, ni sur la performance.

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

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 !

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

J'espère que cet article vous aura plus, et à bientôt sur le blog !

À propos de l'auteur

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

Aujourd'hui je suis aussi 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 !