deepkit/type pour TypeScript : guide complet avec exemples
Dans le monde de TypeScript, il existe un problème fondamental : les types n'existent que lors de la compilation. Après la transpilation en JavaScript, toutes les informations de type disparaissent. Cela rend impossible la validation à l'exécution, la sérialisation dynamique ou la construction de systèmes complexes dépendant des métadonnées de type. La bibliothèque deepkit/type résout ce problème de manière radicale : elle transporte le système de types TypeScript dans l'exécution, permettant de travailler avec les types comme avec des données.
La valeur principale de deepkit/type réside dans le fait qu'elle ne nécessite pas d'écrire des schémas redondants (comme dans Zod ou Yup). Vous décrivez les types une seule fois — en TypeScript pur — et la bibliothèque les extrait automatiquement à l'exécution. Cela permet d'effectuer la validation des données, la sérialisation/désérialisation, la réflexion (obtention d'informations sur les propriétés, les génériques, les décorateurs) et même de générer du code basé sur les types. deepkit/type n'est pas simplement un validateur, c'est un framework complet pour la métaprogrammation.
La bibliothèque est idéale pour les projets où une typage strict est essentiel à tous les niveaux : serveurs API (validation des requêtes entrantes), bases de données (mappage de types en SQL), fichiers de configuration, systèmes RPC et outils de génération de code. Grâce à son intégration avec deepkit/framework, elle est également utilisée pour créer des applications serveur haute performance avec validation et sérialisation automatiques.
Installation
deepkit/type est un package npm pour TypeScript. L'installation se fait avec la commande standard :
npm install @deepkit/type reflect-metadata
Vous devrez également configurer tsconfig.json pour prendre en charge les décorateurs et l'émission de métadonnées :
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"strict": true,
"target": "ES2020",
"module": "commonjs",
"outDir": "./dist"
}
}
Si vous utilisez ts-node pour l'exécution, assurez-vous qu'il charge reflect-metadata en premier. Ajoutez au début de votre fichier d'entrée :
import 'reflect-metadata';
Démarrage rapide
Un exemple minimal montre comment déclarer un type et obtenir sa représentation à l'exécution :
import 'reflect-metadata';
import { typeOf, ReflectionClass } from '@deepkit/type';
// Déclaration de l'interface
interface User {
id: number;
name: string;
email?: string;
}
// Obtention du type à l'exécution
const userType = typeOf();
console.log(userType); // ReflectionClass
// Vérification des propriétés
const reflection = ReflectionClass.from(userType);
console.log(reflection.getProperty('name').type); // ReflectionProperty
console.log(reflection.getProperty('id').type.kind); // 'number'
Ce code affichera les informations sur le type User dans la console. Notez que nous n'avons écrit aucun schéma — seulement une interface TypeScript pure.
Méthodes et fonctions principales
Voici les fonctionnalités clés de deepkit/type. Pour chaque méthode, la signature, une brève description et un exemple fonctionnel sont fournis.
1. typeOf
Signature : typeOf<T>(): ReflectionClass
Ce qu'elle fait : Extrait la représentation à l'exécution du type T. Retourne un objet ReflectionClass contenant toutes les informations sur la structure du type : propriétés, leurs types, modificateurs (optional, readonly), génériques et décorateurs.
import { typeOf } from '@deepkit/type';
interface Product {
id: number;
title: string;
price: number;
}
const productType = typeOf();
console.log(productType.getProperty('title').type.kind); // 'string'
console.log(productType.getProperty('price').type.kind); // 'number'
2. is
Signature : is<T>(value: unknown): value is T
Ce qu'elle fait : Vérifie si une valeur correspond au type T à l'exécution. Fonctionne comme un prédicat de type TypeScript, ce qui permet une inférence de type automatique après la vérification.
import { is } from '@deepkit/type';
interface User {
id: number;
name: string;
}
const data: unknown = { id: 1, name: 'Alice' };
if (is(data)) {
// TypeScript sait maintenant que data est de type User
console.log(data.name.toUpperCase()); // 'ALICE'
}
3. cast
Signature : cast<T>(value: unknown): T
Ce qu'elle fait : Convertit une valeur en un type T, en effectuant les transformations nécessaires (par exemple, conversion de chaînes en nombres, application de valeurs par défaut). Contrairement à is, cast ne lève pas d'erreur mais tente de transformer la valeur pour qu'elle corresponde au type.
import { cast } from '@deepkit/type';
interface Config {
port: number;
host: string;
}
const raw = { port: '3000', host: 'localhost' };
const config = cast(raw);
console.log(config.port); // 3000 (nombre, pas une chaîne)
4. serialize
Signature : serialize<T>(value: T): any
Ce qu'elle fait : Convertit une valeur typée en un objet JSON simple, en supprimant les propriétés non définies dans le type et en appliquant les transformations de sérialisation (comme la conversion de dates en chaînes).
import { serialize } from '@deepkit/type';
interface User {
id: number;
name: string;
createdAt: Date;
}
const user: User = { id: 1, name: 'Bob', createdAt: new Date() };
const json = serialize(user);
console.log(json); // { id: 1, name: 'Bob', createdAt: '2024-01-15T10:30:00.000Z' }
5. deserialize
Signature : deserialize<T>(value: any): T
Ce qu'elle fait : Convertit un objet JSON simple en une instance typée T, en restaurant les types complexes (comme les dates, les tableaux typés, etc.).
import { deserialize } from '@deepkit/type';
interface User {
id: number;
name: string;
createdAt: Date;
}
const json = { id: 1, name: 'Bob', createdAt: '2024-01-15T10:30:00.000Z' };
const user = deserialize(json);
console.log(user.createdAt instanceof Date); // true
6. ReflectionClass
Signature : ReflectionClass.from(type: ReflectionClass): ReflectionClass
Ce qu'elle fait : Fournit une API complète pour inspecter la structure d'un type : lister les propriétés, leurs types, les décorateurs, les génériques, et même les méthodes. C'est la classe centrale pour la réflexion avancée.
import { typeOf, ReflectionClass } from '@deepkit/type';
interface Product {
id: number;
title: string;
price: number;
tags?: string[];
}
const productType = typeOf();
const reflection = ReflectionClass.from(productType);
// Liste des propriétés
reflection.getProperties().forEach(prop => {
console.log(`${prop.name}: ${prop.type.kind} (optional: ${prop.optional})`);
});
// Affiche :
// id: number (optional: false)
// title: string (optional: false)
// price: number (optional: false)
// tags: array (optional: true)
Cas d'utilisation avancés
Validation de requêtes API
deepkit/type excelle dans la validation des données entrantes d'une API REST :
import { is, cast } from '@deepkit/type';
interface CreateUserRequest {
name: string;
email: string;
age: number;
}
app.post('/users', (req, res) => {
const body = req.body;
if (!is(body)) {
return res.status(400).json({ error: 'Données invalides' });
}
const user = cast(body);
// user est maintenant typé et validé
createUser(user);
});
Génération de schémas OpenAPI
Grâce à la réflexion, vous pouvez générer automatiquement des schémas OpenAPI à partir de vos types TypeScript :
import { typeOf, ReflectionClass } from '@deepkit/type';
interface User {
id: number;
name: string;
email: string;
}
function generateOpenApiSchema(): object {
const reflection = ReflectionClass.from(typeOf());
const properties: Record = {};
reflection.getProperties().forEach(prop => {
properties[prop.name] = { type: prop.type.kind };
});
return {
type: 'object',
properties,
required: reflection.getProperties()
.filter(p => !p.optional)
.map(p => p.name)
};
}
const userSchema = generateOpenApiSchema();
console.log(JSON.stringify(userSchema, null, 2));
Conclusion
deepkit/type transforme la façon dont les développeurs TypeScript abordent la validation, la sérialisation et la réflexion. En éliminant le besoin de schémas redondants, elle réduit la duplication de code et améliore la maintenabilité. Que vous construisiez une API, un ORM ou un outil de génération de code, deepkit/type offre une solution élégante et performante pour travailler avec les types à l'exécution.