Introduction : Le mythe de la « conversion magique » des types
\n\nChaque développeur TypeScript rencontre tôt ou tard cette situation : vous recevez du JSON depuis le serveur, vous êtes sûr de connaître sa structure, et vous faites un simple cast avec as. Et… rien ne fonctionne. Les méthodes de la classe sont absentes, les champs sont undefined, et le débogage devient un véritable parcours du combattant.
La question « How do I cast a JSON object to a TypeScript class? » est l'une des plus populaires sur Stack Overflow (des dizaines de milliers de vues). Et ce n'est pas surprenant. TypeScript est une surcouche qui n'existe qu'au moment de la compilation. À l'exécution, votre « classe » n'est qu'un simple objet JavaScript.
\n\nDans cet article, nous allons analyser les 5 erreurs les plus fréquentes et les plus dangereuses que commettent les développeurs lorsqu'ils travaillent avec du JSON et des classes TypeScript. Vous apprendrez à les éviter et obtiendrez des solutions prêtes pour le code de production.
\n\nErreur n°1 : Utiliser as pour une « vraie » conversion
\n\nLe piège le plus courant est de penser que la construction jsonData as MyClass crée une instance de classe.
Que se passe-t-il réellement ?
\nL'opérateur as (ou la syntaxe avec chevrons) est une assertion de type. Il indique au compilateur : « Fais-moi confiance, je connais le type ». Mais en JavaScript, il n'effectue aucune magie.
class User {\n name: string = '';\n greet(): string {\n return `Bonjour, ${this.name} !`;\n }\n}\n\nconst rawData = JSON.parse('{"name": "Anna"}');\nconst user = rawData as User;\n\nconsole.log(user.name); // "Anna" — fonctionne\nconsole.log(user.greet()); // TypeError: user.greet is not a function\n\n\nPourquoi ? Parce que rawData est un simple objet { name: 'Anna' }. Il n'a pas le prototype User.prototype, et donc pas la méthode greet.
Comment corriger ?
\nCréez toujours une véritable instance de classe. La méthode de base est Object.assign :
const user = Object.assign(new User(), rawData);\nconsole.log(user.greet()); // "Bonjour, Anna !"\n\n\nMais cette méthode a des nuances dont nous parlerons plus loin.
\n\nErreur n°2 : Ignorer les objets et tableaux imbriqués
\n\nVous utilisez joyeusement Object.assign, mais vos données contiennent des structures imbriquées. Et là, l'enfer commence.
class Address {\n city: string = '';\n zip: string = '';\n}\n\nclass Company {\n name: string = '';\n address: Address = new Address();\n}\n\nconst data = JSON.parse('{"name": "TechCorp", "address": {"city": "Moscou", "zip": "101000"}}');\nconst company = Object.assign(new Company(), data);\n\nconsole.log(company.address.city); // "Moscou" — chanceux ?\nconsole.log(company.address instanceof Address); // false !\n\n\nProblème : address reste un simple objet, et non une instance de Address. Si Address a des méthodes ou des propriétés calculées, elles seront perdues.
Solution : Conversion récursive
\nÉcrivez une fonction factory ou utilisez la bibliothèque class-transformer :
import { plainToClass } from 'class-transformer';\n\nconst company = plainToClass(Company, data);\nconsole.log(company.address instanceof Address); // true\n\n\nclass-transformer parcourt récursivement tous les champs et crée les instances de classe correctes en fonction des décorateurs @Type.
Erreur n°3 : Perte des valeurs par défaut
\n\nTypeScript permet de définir des valeurs par défaut pour les champs directement dans la classe. Mais lors du « cast » du JSON, ces valeurs peuvent être écrasées par undefined.
class Config {\n theme: string = 'light';\n fontSize: number = 14;\n language: string = 'fr';\n}\n\nconst partialData = JSON.parse('{"theme": "dark"}');\nconst config = Object.assign(new Config(), partialData);\n\nconsole.l