Hone logo
Hone
Problems

Reflecting on Metadata: A TypeScript Challenge

Metadata reflection allows you to inspect and manipulate data about your code at runtime. This is incredibly useful for things like validation, serialization, documentation generation, and building frameworks. This challenge asks you to create a system for defining and retrieving metadata associated with TypeScript classes and their properties.

Problem Description

You need to build a system that allows you to attach metadata to TypeScript classes and their properties. This metadata should be accessible at runtime via reflection. The system should support:

  1. Defining Metadata: A decorator function that can be applied to classes and properties to associate metadata. The decorator should accept a metadata object as an argument.
  2. Retrieving Metadata: A function that takes a class or property and returns its associated metadata. If no metadata is found, it should return undefined.
  3. Metadata Structure: Metadata should be stored in a structured format. For classes, metadata should be an object. For properties, metadata should be an object.
  4. Type Safety: The system should be type-safe, ensuring that the metadata you retrieve matches the type of metadata you defined.

Key Requirements:

  • The solution must use TypeScript decorators.
  • The solution must be able to handle metadata for both classes and properties.
  • The solution must be able to retrieve metadata for classes and properties.
  • The solution must be type-safe.
  • The solution should be extensible to support different types of metadata in the future.

Expected Behavior:

  • Applying the decorator should associate metadata with the decorated class or property.
  • Calling the retrieval function with a class should return the class's metadata.
  • Calling the retrieval function with a property should return the property's metadata.
  • Calling the retrieval function with an object that has no metadata should return undefined.

Edge Cases to Consider:

  • Metadata applied to properties of a class.
  • Multiple decorators applied to the same class or property (consider how to handle this – e.g., overwrite, merge, or throw an error). For this challenge, overwrite is acceptable.
  • Classes extending other classes (metadata inheritance is not required for this challenge).
  • Metadata of different types being associated with the same class or property (type safety is key here).

Examples

Example 1:

// Metadata definition
const myMetadata = {
  description: "This is a sample class",
  version: "1.0.0"
};

// Class decorated with metadata
@MetadataClass(myMetadata)
class SampleClass {
  @MetadataProperty({ name: "sampleProperty", type: "string" })
  sampleProperty: string = "Hello";
}

// Retrieving metadata
const classMetadata = getClassMetadata(SampleClass);
const propertyMetadata = getPropertyMetadata(SampleClass.prototype, "sampleProperty");

// Expected Output:
// classMetadata: { description: "This is a sample class", version: "1.0.0" }
// propertyMetadata: { name: "sampleProperty", type: "string" }

Explanation: The MetadataClass decorator applies metadata to the SampleClass. The MetadataProperty decorator applies metadata to the sampleProperty property. The getClassMetadata and getPropertyMetadata functions retrieve this metadata.

Example 2:

// Metadata definition
const propertyMetadata = {
  required: true,
  maxLength: 255
};

class User {
  @MetadataProperty(propertyMetadata)
  username: string;
}

const userMetadata = getPropertyMetadata(User.prototype, "username");

// Expected Output:
// userMetadata: { required: true, maxLength: 255 }

Explanation: This demonstrates metadata applied to a single property.

Example 3: (Edge Case - No Metadata)

class EmptyClass {}

const noMetadata = getClassMetadata(EmptyClass);

// Expected Output:
// noMetadata: undefined

Explanation: This demonstrates the case where no metadata is defined for a class.

Constraints

  • The solution must be written in TypeScript.
  • The solution must use decorators.
  • The solution must be type-safe.
  • The solution should be reasonably performant (avoid unnecessary iterations or complex logic). Performance is not the primary focus, but avoid obviously inefficient solutions.
  • The metadata object can be any valid JavaScript object.

Notes

  • Consider using Reflect.getMetadataKeys and Reflect.getMetadata as a starting point, but you are not required to use them. The goal is to demonstrate understanding of decorators and metadata reflection.
  • Think about how to structure your code to make it extensible and maintainable.
  • Focus on clarity and type safety.
  • The decorator function should accept a metadata object as its argument.
  • The retrieval function should return undefined if no metadata is found.
  • This challenge focuses on the core concepts of metadata reflection. Advanced features like metadata inheritance or validation are not required.
Loading editor...
typescript