Les décorateurs dans TypeScript fournissent un mécanisme puissant pour modifier le comportement des classes, des méthodes, des propriétés et des paramètres. Bien qu’ils puissent sembler une commodité moderne, les décorateurs sont ancrés dans le modèle de décorateur bien établi que l’on trouve dans la programmation orientée objet. En faisant abstraction des fonctionnalités courantes telles que la journalisation, la validation ou le contrôle d'accès, les décorateurs permettent aux développeurs d'écrire un code plus propre et plus maintenable.
Dans cet article, nous explorerons les décorateurs à partir des principes de base, décomposerons leurs fonctionnalités de base et les implémenterons à partir de zéro. En cours de route, nous examinerons quelques applications du monde réel qui mettent en valeur l'utilité des décorateurs dans le développement quotidien de TypeScript.
En TypeScript, un décorateur est simplement une fonction qui peut être attachée à une classe, une méthode, une propriété ou un paramètre. Cette fonction est exécutée au moment de la conception, vous donnant la possibilité de modifier le comportement ou la structure du code avant son exécution. Les décorateurs permettent la méta-programmation, nous permettant d'ajouter des fonctionnalités supplémentaires sans modifier la logique d'origine.
Commençons par un exemple simple de décorateur de méthode qui enregistre lorsqu'une méthode est appelée :
function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) { const originalMethod = descriptor.value; descriptor.value = function (...args: any[]) { console.log(`Calling ${propertyKey} with arguments: ${args}`); return originalMethod.apply(this, args); }; return descriptor; } class Example { @log greet(name: string) { return `Hello, ${name}`; } } const example = new Example(); example.greet('John');
Ici, le décorateur de journal encapsule la méthode greet, en enregistrant son invocation et ses paramètres avant de l'exécuter. Ce modèle est utile pour séparer les préoccupations transversales telles que la journalisation de la logique de base.
Les décorateurs dans TypeScript sont des fonctions qui récupèrent les métadonnées liées à l'élément qu'ils décorent. Sur la base de ces métadonnées (comme les prototypes de classe, les noms de méthodes ou les descripteurs de propriétés), les décorateurs peuvent modifier le comportement ou même remplacer l'objet décoré.
Les décorateurs peuvent être appliqués à différentes cibles, chacune ayant des objectifs différents :
function classDecorator(constructor: Function) { // Modify or extend the class constructor or prototype }
function methodDecorator(target: any, propertyKey: string, descriptor: PropertyDescriptor) { // Modify the method's descriptor }
function propertyDecorator(target: any, propertyKey: string) { // Modify the behavior of the property }
function parameterDecorator(target: any, propertyKey: string, parameterIndex: number) { // Modify or inspect the method's parameter }
L'une des caractéristiques les plus puissantes des décorateurs est leur capacité à accepter les arguments, vous permettant de personnaliser leur comportement. Par exemple, créons un décorateur de méthode qui enregistre les appels de méthode de manière conditionnelle en fonction d'un argument.
function logConditionally(shouldLog: boolean) { return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) { const originalMethod = descriptor.value; descriptor.value = function (...args: any[]) { if (shouldLog) { console.log(`Calling ${propertyKey} with arguments: ${args}`); } return originalMethod.apply(this, args); }; return descriptor; }; } class Example { @logConditionally(true) greet(name: string) { return `Hello, ${name}`; } } const example = new Example(); example.greet('TypeScript Developer');
En passant true au décorateur logConditionally, nous nous assurons que la méthode journalise son exécution. Si nous passons false, la journalisation est ignorée. Cette flexibilité est essentielle pour rendre les décorateurs polyvalents et réutilisables.
Les décorateurs ont trouvé une utilisation pratique dans de nombreuses bibliothèques et frameworks. Voici quelques exemples notables qui illustrent comment les décorateurs rationalisent des fonctionnalités complexes :
import { IsEmail, IsNotEmpty } from 'class-validator'; class User { @IsNotEmpty() name: string; @IsEmail() email: string; }
Dans cet exemple, les décorateurs @IsEmail et @IsNotEmpty s'assurent que le champ email est une adresse e-mail valide et que le champ nom n'est pas vide. Ces décorateurs gagnent du temps en éliminant le besoin d'une logique de validation manuelle.
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm'; @Entity() class User { @PrimaryGeneratedColumn() id: number; @Column() name: string; @Column() email: string; }
Ici, @Entity, @Column et @PrimaryGeneratedColumn définissent la structure de la table User. Ces décorateurs éliminent la complexité de la création de tables SQL, rendant le code plus lisible et maintenable.
@Injectable({ providedIn: 'root', }) class UserService { constructor(private http: HttpClient) {} }
Le décorateur @Injectable dans ce cas signale au système d'injection de dépendances d'Angular que le UserService doit être fourni globalement. Cela permet une intégration transparente des services dans l'ensemble de l'application.
Les décorateurs ne sont, à la base, que des fonctions. Décomposons le processus de création de décorateurs à partir de zéro :
A class decorator receives the constructor of the class and can be used to modify the class prototype or even replace the constructor.
function AddTimestamp(constructor: Function) { constructor.prototype.timestamp = new Date(); } @AddTimestamp class MyClass { id: number; constructor(id: number) { this.id = id; } } const instance = new MyClass(1); console.log(instance.timestamp); // Outputs the current timestamp
A method decorator modifies the method descriptor, allowing you to alter the behavior of the method itself.
function logExecutionTime(target: any, propertyKey: string, descriptor: PropertyDescriptor) { const originalMethod = descriptor.value; descriptor.value = function (...args: any[]) { const start = performance.now(); const result = originalMethod.apply(this, args); const end = performance.now(); console.log(`${propertyKey} executed in ${end - start}ms`); return result; }; return descriptor; } class Service { @logExecutionTime execute() { // Simulate work for (let i = 0; i < 1e6; i++) {} } } const service = new Service(); service.execute(); // Logs the execution time
A property decorator allows you to intercept property access and modification, which can be useful for tracking changes.
function trackChanges(target: any, propertyKey: string) { let value = target[propertyKey]; const getter = () => value; const setter = (newValue: any) => { console.log(`${propertyKey} changed from ${value} to ${newValue}`); value = newValue; }; Object.defineProperty(target, propertyKey, { get: getter, set: setter, }); } class Product { @trackChanges price: number; constructor(price: number) { this.price = price; } } const product = new Product(100); product.price = 200; // Logs the change
Decorators in TypeScript allow you to abstract and reuse functionality in a clean, declarative manner. Whether you're working with validation, ORMs, or dependency injection, decorators help reduce boilerplate and keep your code modular and maintainable. Understanding how they work from first principles makes it easier to leverage their full potential and craft custom solutions tailored to your application.
By taking a deeper look at the structure and real-world applications of decorators, you've now seen how they can simplify complex coding tasks and streamline code across various domains.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!