Hone logo
Hone
Problems

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:

  1. Decorator Factory: Create a function createMetadataDecorator that accepts a key (string) and a value (any) as arguments.
  2. Returns Decorator: createMetadataDecorator must return a function that conforms to the TypeScript class decorator signature: (target: Function) => void.
  3. Metadata Attachment: The returned decorator function should attach the value under the key to the target (the class constructor). A common and effective way to do this is by adding properties directly to the constructor function object.
  4. Metadata Retrieval: Implement a separate function getMetadata that takes a target (class constructor) and a key (string) and returns the associated metadata value. If no metadata is found for that key, it should return undefined.

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 getMetadata is 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 key parameter for the decorator factory will always be a string.
  • The value parameter can be of any type.
  • Your solution should work in a standard TypeScript environment with decorators enabled (experimentalDecorators: true in tsconfig.json).

Notes

  • Remember that class decorators in TypeScript receive the class constructor function as their target argument.
  • Consider how you can make the metadata attachment unique to the specific key. Using Symbol could 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 getMetadata function should safely handle cases where the target might not have any metadata attached.
Loading editor...
typescript