TypeScript Class Decorator Factory for Metadata Tagging
This challenge will test your understanding of TypeScript class decorators, specifically focusing on creating a decorator factory. You'll build a system that allows you to attach arbitrary metadata to classes, which can then be accessed and utilized by other parts of your application. This is a common pattern for frameworks and libraries to add configuration or behavioral extensions to user-defined classes.
Problem Description
Your task is to implement a TypeScript class decorator factory that can be used to add specific metadata to classes. This metadata will be stored in a way that can be easily retrieved later.
What needs to be achieved: You need to create a function that acts as a "decorator factory." This factory function will accept arguments and return the actual class decorator function. The returned decorator function will then be applied to a class and will attach the provided metadata to that class.
Key Requirements:
- Decorator Factory: Create a function
createMetadataDecoratorthat accepts akey(string) and avalue(any) as arguments. - Returns Decorator:
createMetadataDecoratormust return a function that conforms to the TypeScript class decorator signature:(target: Function) => void. - Metadata Attachment: The returned decorator function should attach the
valueunder thekeyto thetarget(the class constructor). A common and effective way to do this is by adding properties directly to the constructor function object. - Metadata Retrieval: Implement a separate function
getMetadatathat takes atarget(class constructor) and akey(string) and returns the associated metadata value. If no metadata is found for that key, it should returnundefined.
Expected Behavior:
When createMetadataDecorator('myKey', 'myValue') is applied to a class MyClass, an instance of MyClass should later be able to retrieve 'myValue' by calling getMetadata(MyClass, 'myKey').
Edge Cases:
- What happens if
getMetadatais called with a key that was never set on the class? - What happens if multiple decorators with the same key but different values are applied to the same class (though the primary goal is to allow distinct keys)?
- Consider classes that have no decorators applied.
Examples
Example 1: Simple Metadata Tagging
// Assume createMetadataDecorator and getMetadata are implemented as required
// Decorator factory usage
@createMetadataDecorator('className', 'User')
@createMetadataDecorator('version', 1.0)
class User {
name: string;
constructor(name: string) {
this.name = name;
}
}
// Retrieval
const userClassName = getMetadata(User, 'className');
const userVersion = getMetadata(User, 'version');
const userEmail = getMetadata(User, 'email'); // Key not set
console.log(userClassName); // Expected: 'User'
console.log(userVersion); // Expected: 1.0
console.log(userEmail); // Expected: undefined
Explanation:
The @createMetadataDecorator('className', 'User') and @createMetadataDecorator('version', 1.0) calls attach the respective key-value pairs to the User class. getMetadata successfully retrieves the values for 'className' and 'version', and returns undefined for 'email' as it was not defined.
Example 2: Metadata for different classes
// Assume createMetadataDecorator and getMetadata are implemented as required
@createMetadataDecorator('apiEndpoint', '/api/v1/products')
class ProductService {
// ... service logic
}
@createMetadataDecorator('apiEndpoint', '/api/v1/users')
class UserService {
// ... service logic
}
const productServiceEndpoint = getMetadata(ProductService, 'apiEndpoint');
const userServiceEndpoint = getMetadata(UserService, 'apiEndpoint');
console.log(productServiceEndpoint); // Expected: '/api/v1/products'
console.log(userServiceEndpoint); // Expected: '/api/v1/users'
Explanation:
This example demonstrates that metadata is specific to each class. Even though both classes use the same key ('apiEndpoint'), the values are distinct and correctly retrieved from their respective classes.
Constraints
- The metadata should be stored directly on the class constructor function object. Avoid using external global variables or complex module-level caches for simplicity in this challenge.
- The
keyparameter for the decorator factory will always be a string. - The
valueparameter can be of any type. - Your solution should work in a standard TypeScript environment with decorators enabled (
experimentalDecorators: trueintsconfig.json).
Notes
- Remember that class decorators in TypeScript receive the class constructor function as their
targetargument. - Consider how you can make the metadata attachment unique to the specific key. Using
Symbolcould be an alternative to plain strings for keys if you wanted to avoid potential naming collisions in a larger application, but for this challenge, string keys are sufficient and simpler. - The
getMetadatafunction should safely handle cases where thetargetmight not have any metadata attached.