YouTip LogoYouTip

Ts Decorators

Decorators are an experimental feature in TypeScript.\\\\n\\\\nIt allows developers to add extra functionality to classes, methods, properties, or parameters without modifying the original class.\\\\n\\\\nA decorator is essentially a function that can be called at runtime to modify the behavior of the target object.\\\\n\\\\n* * *\\\\n\\\\n## Decorator Introduction\\\\n\\\\nDecorators use the `@` symbol as syntactic sugar and can be attached to classes, methods, accessors, properties, or parameters.\\\\n\\\\nThis pattern is commonly used in framework development; for example, Angular and TypeORM heavily use decorators to implement features like dependency injection and data validation.\\\\n\\\\n> **Note:** Decorators are currently an experimental feature and need to be explicitly enabled in tsconfig.json. When using them in a production environment, please confirm the level of support for experimental features in your project.\\\\n\\\\n* * *\\\\n\\\\n## Configuring and Enabling Decorators\\\\n\\\\nBefore using decorators, you need to enable the relevant compilation options in the TypeScript configuration file tsconfig.json.\\\\n\\\\n## tsconfig.json Configuration\\\\n\\\\n{\\\\n\\\\n"compilerOptions":{\\\\n\\\\n// Enable decorator syntax\\\\n\\\\n"experimentalDecorators":true,\\\\n\\\\n// Enable decorator metadata (used for dependency injection frameworks)\\\\n\\\\n"emitDecoratorMetadata":true\\\\n\\\\n}\\\\n\\\\n}\\\\n\\\\n**Parameter Description:**\\\\n\\\\n* **experimentalDecorators:** Enables decorator syntax support, which is a prerequisite for using decorators\\\\n* **emitDecoratorMetadata:** Generates decorator metadata in the compiled JavaScript, for use by dependency injection frameworks\\\\n\\\\n* * *\\\\n\\\\n## Class Decorators\\\\n\\\\nClass decorators are applied to the constructor of a class and can modify the class definition or add additional processing logic.\\\\n\\\\nA class decorator receives one parameter, which is the constructor of the target class.\\\\n\\\\n## Basic Usage of Class Decorators\\\\n\\\\n// Define a class decorator function\\\\n\\\\n// The parameter target is the decorated class constructor\\\\n\\\\nfunction sealed(target:Function){\\\\n\\\\n// Print the name of the class the decorator is applied to\\\\n\\\\n console.log("Decorators applied to: "+ target.name);\\\\n\\\\n// Use Object.seal to lock the constructor and prototype\\\\n\\\\n// Prevent adding or deleting properties at runtime\\\\n\\\\nObject.seal(target);\\\\n\\\\nObject.seal(target.prototype);\\\\n\\\\n}\\\\n\\\\n// Apply the decorator to the class using @ syntax\\\\n\\\\n@sealed\\\\n\\\\nclass Person {\\\\n\\\\n name: string;\\\\n\\\\nconstructor(name: string){\\\\n\\\\nthis.name= name;\\\\n\\\\n}\\\\n\\\\n}\\\\n\\\\n// Create an instance for testing\\\\n\\\\nvar person =new Person("TUTORIAL");\\\\n\\\\n console.log("create: "+ person.name);\\\\n\\\\n// Try to add a new property (will be prevented because the class is sealed)\\\\n\\\\n// person.age = 25; // Fails silently\\\\n\\\\n**Execution Result:**\\\\n\\\\nDecorators applied to: Personcreate: TUTORIAL\\\\n**Explanation:**\\\\n\\\\nClass decorators execute when the class is defined, and are typically used to modify class behavior, add metadata, or implement AOP (Aspect-Oriented Programming).\\\\n\\\\n* * *\\\\n\\\\n## Method Decorators\\\\n\\\\nMethod decorators are applied to class methods and can modify the method's Property Descriptor.\\\\n\\\\nA method decorator receives three parameters: the target object, the property name, and the property descriptor.\\\\n\\\\n## Basic Usage of Method Decorators\\\\n\\\\n// Define a method decorator factory\\\\n\\\\n// Returns a decorator function\\\\n\\\\nfunction enumerable(value:boolean){\\\\n\\\\n// Returns the decorator function, receiving three parameters\\\\n\\\\nreturn function(\\\\n\\\\n target: any,// The prototype object of the owning class\\\\n\\\\n propertyKey: string,// The method name\\\\n\\\\n descriptor: PropertyDescriptor // The property descriptor\\\\n\\\\n){\\\\n\\\\n// Modify the enumerable characteristic of the property\\\\n\\\\n// false means the method is not enumerable\\\\n\\\\n descriptor.enumerable= value;\\\\n\\\\n};\\\\n\\\\n}\\\\n\\\\nclass Greeter {\\\\n\\\\n greeting: string;\\\\n\\\\nconstructor(message: string){\\\\n\\\\nthis.greeting= message;\\\\n\\\\n}\\\\n\\\\n// Apply the decorator, setting the method as non-enumerable\\\\n\\\\n@enumerable(false)\\\\n\\\\n greet(){\\\\n\\\\nreturn"Hello, "+this.greeting;\\\\n\\\\n}\\\\n\\\\n}\\\\n\\\\nvar g =new Greeter("World");\\\\n\\\\n// Check if the greet method is enumerable\\\\n\\\\n console.log("Method Enumerable: "+ g.propertyIsEnumerable("greet"));\\\\n\\\\n// Iterate over the object's properties\\\\n\\\\nfor(var key in g){\\\\n\\\\n console.log("property: "+ key);\\\\n\\\\n}\\\\n\\\\n**Execution Result:**\\\\n\\\\nMethod Enumerable: false\\\\n> **Tip:** PropertyDescriptor contains properties like enumerable, configurable, writable, and value, which can be modified as needed.\\\\n\\\\n* * *\\\\n\\\\n## Accessor Decorators\\\\n\\\\nAccessor decorators are applied to the getter and setter methods of a class.\\\\n\\\\nSimilar to method decorators, accessor decorators can also modify the property descriptor.\\\\n\\\\n## Accessor Decorator Usage\\\\n\\\\n// Accessor decorator factory\\\\n\\\\nfunction configurable(value:boolean){\\\\n\\\\nreturn function(\\\\n\\\\n target: any,\\\\n\\\\n propertyKey: string,\\\\n\\\\n descriptor: PropertyDescriptor\\\\n\\\\n){\\\\n\\\\n// Modify the configurable characteristic of the property\\\\n\\\\n// false means the accessor cannot be reconfigured or deleted\\\\n\\\\n descriptor.configurable= value;\\\\n\\\\n};\\\\n\\\\n}\\\\n\\\\nclass Point {\\\\n\\\\n private _x: number =0;\\\\n\\\\n private _y: number =0;\\\\n\\\\n// Use the decorator to lock the getter\\\\n\\\\n@configurable(false)\\\\n\\\\nget x(){\\\\n\\\\nreturn this._x;\\\\n\\\\n}\\\\n\\\\n@configurable(false)\\\\n\\\\nget y(){\\\\n\\\\nreturn this._y;\\\\n\\\\n}\\\\n\\\\nset x(value: number){\\\\n\\\\nthis._x = value;\\\\n\\\\n}\\\\n\\\\nset y(value: number){\\\\n\\\\nthis._y = value;\\\\n\\\\n}\\\\n\\\\n}\\\\n\\\\nvar point =new Point();\\\\n\\\\n point.x=10;\\\\n\\\\n point.y=20;\\\\n\\\\n console.log("Coordinates: ("+ point.x+", "+ point.y+")");\\\\n\\\\n**Explanation:**\\\\n\\\\nAccessor decorators cannot be applied to both the getter and setter of the same property simultaneously; you must choose only one.\\\\n\\\\n* * *\\\\n\\\\n## Property Decorators\\\\n\\\\nProperty decorators are applied to the property definition of a class.\\\\n\\\\nA property decorator receives two parameters: the target object and the property name.\\\\n\\\\n## Property Decorator Usage\\\\n\\\\n// Property decorator factory\\\\n\\\\nfunction format(formatString: string){\\\\n\\\\nreturn function(\\\\n\\\\n target: any,// The prototype object of the class\\\\n\\\\n propertyKey: string // The property name\\\\n\\\\n){\\\\n\\\\n// Store metadata on the target object\\\\n\\\\n// Use propertyKey + "_format" as the key name to avoid conflicts\\\\n\\\\nObject.defineProperty(target, propertyKey +"_format",{\\\\n\\\\n value: formatString,\\\\n\\\\n writable:false,\\\\n\\\\n enumerable:false,\\\\n\\\\n configurable:true\\\\n\\\\n});\\\\n\\\\n};\\\\n\\\\n}\\\\n\\\\nclass User {\\\\n\\\\n// Apply the property decorator, specifying the date format\\\\n\\\\n@format("YYYY-MM-DD")\\\\n\\\\n birthDate: string;\\\\n\\\\nconstructor(birthDate: string){\\\\n\\\\nthis.birthDate= birthDate;\\\\n\\\\n}\\\\n\\\\n}\\\\n\\\\nvar user =new User("1990-01-01");\\\\n\\\\n console.log("Birth Date: "+ user.birthDate);\\\\n\\\\n// Access the stored metadata\\\\n\\\\n console.log("Date Format: "+(user as any).birthDate_format);\\\\n\\\\n**Execution Result:**\\\\n\\\\nBirth Date: 1990-01-01Date Format: YYYY-MM-DD\\\\n\\\\n* * *\\\\n\\\\n## Parameter Decorators\\\\n\\\\nParameter decorators are applied to the parameters of class methods and can add metadata or markers to the parameters.\\\\n\\\\nA parameter decorator receives three parameters: the target object, the method name, and the index of the parameter in the function.\\\\n\\\\n## Parameter Decorator Usage\\\\n\\\\n// Parameter decorator\\\\n\\\\n// Used to record parameter information or perform validation\\\\n\\\\nfunction logParameter(\\\\n\\\\n target: any,// The prototype object of the class\\\\n\\\\n propertyKey: string,// The method name\\\\n\\\\n parameterIndex: number // The index position of the parameter in the function (starting from 0)\\\\n\\\\n){\\\\n\\\\n console.log("Parameter Decorators: "+ propertyKey +\\\\n\\\\n" Line "+(parameterIndex +1)+" Parameters");\\\\n\\\\n}\\\\n\\\\nclass Greeter {\\\\n\\\\n greeting: string;\\\\n\\\\nconstructor(greeting: string){\\\\n\\\\nthis.greeting= greeting;\\\\n\\\\n}\\\\n\\\\n// Apply the decorator using @ syntax before the parameter\\\\n\\\\n greet(@logParameter name: string){\\\\n\\\\nreturn this.greeting+", "+ name;\\\\n\\\\n}\\\\n\\\\n}\\\\n\\\\nvar greeter =new Greeter("Hello");\\\\n\\\\n greeter.greet("TUTORIAL");\\\\n\\\\n**Execution Result:**\\\\n\\\\nParameter Decorators: greet Line 1 Parameters\\\\n\\\\n* * *\\\\n\\\\n## Decorator Factories\\\\n\\\\nDecorator factories are functions that return decorator functions.\\\\n\\\\nThrough decorator factories, you can pass custom parameters when applying decorators, achieving more flexible configuration.\\\\n\\\\n## Decorator Factory Implementation: Colored Logs\\\\n\\\\n// Decorator factory: receives configuration parameters, returns a decorator function\\\\n\\\\nfunction color(colorCode: string){\\\\n\\\\n// colorCode is the ANSI escape sequence color code\\\\n\\\\n// For example: 34 = blue, 31 = red, 32 = green\\\\n\\\\nreturn function(\\\\n\\\\n target: any,\\\\n\\\\n propertyKey: string,\\\\n\\\\n descriptor: PropertyDescriptor\\\\n\\\\n){\\\\n\\\\n// Save the original method\\\\n\\\\nvar originalMethod = descriptor.value;\\\\n\\\\n// Override the method, adding color\\\\n\\\\n descriptor.value=function(...args: any[]){\\\\n\\\\n// Call the original method to get the return value\\\\n\\\\nvar result = originalMethod.apply(this, args);\\\\n\\\\n// If in a terminal environment, add color to the output\\\\n\\\\n// ANSI escape sequence format: \\\\\\\\x1bm content \\\\\\\\x1b[0m\\\\n\\\\nreturn"\\\\\\\\x 1b["+ colorCode +"m"+ result +"\\\\\\\\x 1b[0m";\\\\n\\\\n};\\\\n\\\\n};\\\\n\\\\n}\\\\n\\\\nclass Logger {\\\\n\\\\n// Use the decorator factory, passing the blue code 34\\\\n\\\\n@color("34")\\\\n\\\\n log(message: string): string {\\\\n\\\\nreturn message;\\\\n\\\\n}\\\\n\\\\n@color("31")\\\\n\\\\n error(message: string): string {\\\\n\\\\nreturn message;\\\\n\\\\n}\\\\n\\\\n@color("32")\\\\n\\\\n success(message: string): string {\\\\n\\\\nreturn message;\\\\n\\\\n}\\\\n\\\\n}\\\\n\\\\nvar logger =new Logger();\\\\n\\\\n console.log(logger.log("This is a blue log"));\\\\n\\\\n console.log(logger.error("This is a red error"));\\\\n\\\\n console.log(logger.success("This is a green success"));\\\\n\\\\n**Execution Result:**\\\\n\\\\nThis is a blue log (displayed in blue in the terminal) This is a red error (displayed in red in the terminal) This is a green success (displayed in green in the terminal)\\\\n> **Explanation:** Decorator factories are the most commonly used form in actual development, as they allow passing parameters when applying decorators, achieving configurability.\\\\n\\\\n* * *\\\\n\\\\n## Decorator Execution Order\\\\n\\\\nWhen there are multiple decorators on a class, the execution order follows specific rules.\\\\n\\\\n* Decorators are applied from bottom to top\\\\n* Multiple decorators of the same type execute from right to left\\\\n* Parameter decorators execute before method decorators\\\\n\\\\n## Decorator Execution Order Example\\\\n\\\\n// Multiple decorators stacked together\\\\n\\\\nfunction first(){\\\\n\\\\n console.log("first Decorators");\\\\n\\\\nreturn function(target: any){\\\\n\\\\n console.log("first Decoratorsfunction");\\\\n\\\\n};
← Ts Optional ChainingTs Access Modifiers β†’