Decorators in TypeScript provide a way to add annotations and a meta-programming syntax for class declarations and members. They are a powerful feature that allows you to modify the behavior of classes, methods, accessors, properties, or parameters. In this section, we will explore how decorators work, their types, and how to implement them in your TypeScript projects.
Key Concepts
-
What are Decorators?
- Decorators are special types of declarations that can be attached to a class, method, accessor, property, or parameter.
- They are a form of syntactic sugar that allows you to apply reusable logic to your code.
-
Types of Decorators:
- Class Decorators: Applied to a class constructor.
- Method Decorators: Applied to a method of a class.
- Accessor Decorators: Applied to a getter or setter of a class.
- Property Decorators: Applied to a property of a class.
- Parameter Decorators: Applied to a parameter of a method.
-
Decorator Factories:
- A decorator factory is a function that returns a decorator function. It allows you to pass parameters to a decorator.
Practical Examples
Class Decorator
A class decorator is a function that takes a class constructor as an argument and can return a new constructor to replace the original one.
function sealed(constructor: Function) { Object.seal(constructor); Object.seal(constructor.prototype); } @sealed class Greeter { greeting: string; constructor(message: string) { this.greeting = message; } greet() { return `Hello, ${this.greeting}`; } }
Explanation:
- The
sealed
decorator seals the constructor and its prototype, preventing new properties from being added.
Method Decorator
A method decorator is a function that is applied to the property descriptor of the method.
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); }; } class Calculator { @log add(a: number, b: number): number { return a + b; } } const calculator = new Calculator(); calculator.add(2, 3); // Logs: Calling add with arguments: 2,3
Explanation:
- The
log
decorator wraps the original method, logging its name and arguments before calling it.
Property Decorator
A property decorator is a function that is applied to a property of a class.
function readonly(target: any, propertyKey: string) { Object.defineProperty(target, propertyKey, { writable: false, }); } class Person { @readonly name: string = "John Doe"; } const person = new Person(); person.name = "Jane Doe"; // Error: Cannot assign to read only property 'name'
Explanation:
- The
readonly
decorator makes thename
property immutable.
Exercises
Exercise 1: Create a Method Decorator
Task: Create a method decorator named measureTime
that logs the execution time of a method.
function measureTime(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(`Execution time for ${propertyKey}: ${end - start}ms`); return result; }; } class MathOperations { @measureTime multiply(a: number, b: number): number { return a * b; } } const operations = new MathOperations(); operations.multiply(5, 10);
Solution Explanation:
- The
measureTime
decorator calculates the time taken to execute themultiply
method and logs it.
Exercise 2: Implement a Class Decorator
Task: Implement a class decorator named logClass
that logs the creation of an instance of the class.
function logClass(constructor: Function) { const original = constructor; function construct(constructor: any, args: any[]) { console.log(`Creating instance of ${constructor.name}`); return new original(...args); } const newConstructor: any = function (...args: any[]) { return construct(original, args); }; newConstructor.prototype = original.prototype; return newConstructor; } @logClass class User { constructor(public name: string) {} } const user = new User("Alice");
Solution Explanation:
- The
logClass
decorator logs a message every time a new instance of theUser
class is created.
Conclusion
Decorators in TypeScript provide a powerful way to add behavior to your classes and their members. By understanding and utilizing decorators, you can write more modular and reusable code. In the next section, we will explore best practices for using TypeScript with Playwright, building on the knowledge of decorators and other advanced TypeScript features.
Playwright with TypeScript: From Beginner to Advanced
Module 1: Introduction to Playwright and TypeScript
- What is Playwright?
- Setting Up Your Development Environment
- Introduction to TypeScript
- Basic TypeScript Syntax
Module 2: Getting Started with Playwright
- Installing Playwright
- Creating Your First Playwright Script
- Understanding Playwright's Core Concepts
- Running Playwright Tests
Module 3: Playwright and TypeScript Basics
- Writing Tests in TypeScript
- Using TypeScript Interfaces and Types
- Debugging Playwright Tests
- Handling Asynchronous Code
Module 4: Advanced Playwright Features
- Working with Selectors
- Handling Multiple Pages and Frames
- Network Interception and Mocking
- Emulating Devices and Geolocation
Module 5: Test Automation Strategies
- Organizing Tests and Test Suites
- Using Fixtures and Hooks
- Parallel Test Execution
- Continuous Integration with Playwright
Module 6: Advanced TypeScript Techniques
- Generics in TypeScript
- Advanced TypeScript Types
- TypeScript Decorators
- TypeScript and Playwright Best Practices
Module 7: Real-World Playwright Applications
- End-to-End Testing with Playwright
- Visual Testing with Playwright
- Performance Testing with Playwright
- Case Study: Implementing Playwright in a Project