How to get class properties and values using Typescript Reflection?


TypeScript is a superset of JavaScript that provides static typing capabilities, allowing developers to write more reliable and efficient code. One of the most powerful features of TypeScript is its support for reflection. Reflection enables TypeScript developers to inspect and manipulate the properties of classes at runtime, making it easier to write more flexible and dynamic code.

In this article, we will explore how to use TypeScript reflection to get class properties and values. We will discuss what reflection is and how it works in TypeScript, provide a brief overview of TypeScript decorators, and then walk through three examples of how to use reflection to get class properties and values.

What is Reflection in TypeScript?

Reflection is a programming language feature that allows developers to inspect and modify the structure and behaviour of code at runtime. Reflection is available in many programming languages, including TypeScript.

In TypeScript, reflection works by using the built-in Reflect object, which provides a set of methods that enable developers to inspect and modify the properties of classes and objects. These methods include Reflect.get, Reflect.set, Reflect.has, and Reflect.getOwnPropertyDescriptor, among others.

Reflection is particularly useful for building frameworks and libraries, where the code needs to be able to adapt to a wide range of use cases and scenarios. Reflection makes it possible to build more flexible and dynamic code that can respond to changes in the environment or user input.

TypeScript Decorators

Before we dive into examples of using reflection to get class properties and values, it's crucial to understand TypeScript decorators. TypeScript decorators are a language feature that allows developers to add metadata to classes, methods, properties, and parameters. This metadata can then be used at runtime using reflection. Currently, TypeScript supports decorators as an experimental feature, and it may change in future. So, to use decorators in TypeScript, presently, we have to enable experimentalDecorators and emitDecoratorMetadata options in the tsconfig.json file.

{
   "compilerOptions": {
      "target": "ES5",
      "experimentalDecorators": true,
	   "emitDecoratorMetadata": true,     
   }
}

Decorators are a way of adding additional functionality to code without modifying the underlying code directly. This is important because it allows developers to build more flexible and reusable code that can be adapted to different use cases without requiring significant additional boilerplate.

Syntax

Decorators are defined using the @ symbol, followed by the name of the decorator. Decorators can be applied to classes, methods, properties, and parameters.

Here is the syntax of a decorator that adds metadata to a class −

@myDecorator
class MyClass {
   // class implementation here
}

In this example syntax, myDecorator is a decorator that has been applied to the MyClass class. The decorator adds additional metadata to the class that can be used at runtime using reflection.

Example 1: Getting Class Property and Value

Now that we have a basic understanding of reflection and decorators let's walk through an example of how to use reflection to get class properties.

In this example, we define a decorator function called MyDecorator that takes a target object and a key as arguments. Inside the decorator function, we create a metadata key using the key parameter and define metadata for that key using the Reflect.defineMetadata method. We then retrieve the metadata for the key using the Reflect.getMetadata method and log the key-value pair to the console using console.log.

Here is the code for MyDecorator decorator −

test.ts

function MyDecorator(target: any, key: string) {
   const metadataKey = `__metadata__${key}`;
   const metadataValue = 'some metadata';

   Reflect.defineMetadata(metadataKey, metadataValue, target);

   const metadata = Reflect.getMetadata(metadataKey, target);
   console.log(`Metadata for ${key}: ${metadata}`);
}

We then define a class called MyClass and decorate its myMethod method with the MyDecorator decorator. Finally, we create an instance of MyClass.

test.ts

class MyClass {
   @MyDecorator
   myMethod() {}
}
const instance = new MyClass();

But, before we try to compile this code, we have to import “reflect-metadata” package from npm. This is because Reflect API is not fully implemented in all JavaScript environments, including Node.js and many browsers. This means that using the Reflect API in TypeScript can lead to runtime errors if the environment does not support it.

The reflect-metadata package provides a polyfill for the missing Reflect API functionality, allowing you to use the Reflect API in all environments. This is achieved by adding support for metadata in JavaScript using a combination of decorators and the Reflect API.

In order to use metadata decorators and the Reflect API in TypeScript, you need to include the reflect-metadata package in your project and import it at the top of your TypeScript files −

Install the reflect-metadata package using npm

$npm i reflect-metadata

Now import it into your project −

import "reflect-metadata"

Here is the complete code −

import "reflect-metadata"
function MyDecorator(target: any, key: string) {
   const metadataKey = `__metadata__${key}`;
   const metadataValue = 'some metadata';

   Reflect.defineMetadata(metadataKey, metadataValue, target);

   const metadata = Reflect.getMetadata(metadataKey, target);
   console.log(`Metadata for ${key}: ${metadata}`);
}
class MyClass {
   @MyDecorator
   myMethod() {}
}
const instance = new MyClass();

Now to run this code, run following commands −

$ tsc -w
$ node test.js

Output

You will see an output like this in the terminal −

Metadata for myMethod: some metadata

This output confirms that our decorator is working correctly and that we can use reflection to get class properties and their values at runtime.

If you’re seeing the following error, there might be some issue with your import of the reflect-metadata package.

TypeError: Reflect.defineMetadata is not a function

Conclusion

In this article, we explored how to use reflection to get class properties and values. With a simple example, we saw how reflection can be used to retrieve metadata associated with class methods.

Reflection is a powerful tool that can be used to add additional functionality to your TypeScript applications. By using decorators and reflection, you can create dynamic and flexible classes that can adapt to changing requirements and environments.

Finally, not all environments may support the Reflect API or metadata decorators, so code that relies on these features may not be portable to all JavaScript environments.

In conclusion, while TypeScript reflection can be a powerful tool for writing more expressive and flexible code, it also has potential drawbacks and should be used with care. When using reflection, it's important to keep performance and maintainability in mind and to ensure that your code is portable across different JavaScript environments.

Updated on: 01-Aug-2023

3K+ Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements