ECMAScript question detail
Decorator Metadata
ES2025 enhances JavaScript decorators by allowing them to associate metadata with decorated elements. This provides a standard way to attach and retrieve metadata for classes, methods, and properties.
// Define a decorator that attaches metadata
function apiEndpoint(path, method = "GET") {
return function(target, context) {
context.metadata = {
...context.metadata,
api: { path, method }
};
return target;
};
}
// Define a class with decorated methods
class UserService {
@apiEndpoint("/users", "GET")
getAllUsers() {
// Implementation...
}
@apiEndpoint("/users/:id", "GET")
getUserById(id) {
// Implementation...
}
@apiEndpoint("/users", "POST")
createUser(name, age) {
// Implementation...
}
}
// Access the metadata
function generateApiDocs(serviceClass) {
const methodNames = Object.getOwnPropertyNames(serviceClass.prototype)
.filter(name => name !== 'constructor');
for (const methodName of methodNames) {
const method = serviceClass.prototype[methodName];
const metadata = method.context?.metadata;
if (metadata && metadata.api) {
console.log(`${metadata.api.method} ${metadata.api.path} - ${methodName}`);
}
}
}
generateApiDocs(UserService);
// Output:
// GET /users - getAllUsers
// GET /users/:id - getUserById
// POST /users - createUser
Decorator Metadata is particularly useful for:
- API Documentation: Automatically generating documentation from code.
- Validation: Defining validation rules for method parameters.
- Dependency Injection: Specifying dependencies for classes and methods.
- Serialization: Defining how objects should be serialized/deserialized.
- Framework Integration: Providing metadata for frameworks to use.
// Example of parameter validation with decorator metadata
function validateParams(target, context) {
const originalMethod = target.value;
const methodMetadata = context.metadata;
target.value = function(...args) {
if (methodMetadata && methodMetadata.params) {
const validations = methodMetadata.params;
for (let i = 0; i < validations.length; i++) {
const validation = validations[i];
const arg = args[i];
if (validation.required && (arg === undefined || arg === null)) {
throw new Error(`Parameter ${i} is required for ${context.name}`);
}
}
}
return originalMethod.apply(this, args);
};
return target;
}