TypeScript and decorators have become an integral part of modern development. Many frameworks like NestJS, TypeORM, and others actively use metadata through reflect-metadata
. However, this approach has a significant drawback - lack of typing.
The Problem
When working directly with reflect-metadata
, TypeScript cannot verify type correctness. This can lead to runtime errors.
1. No type validation during storage:
// We can store anything - TypeScript won't object
Reflect.defineMetadata('userAge', 'not a number!', Target);
Reflect.defineMetadata('userInfo', null, Target);
Reflect.defineMetadata('config', undefined, Target);
2. Loss of type information when retrieving data:
// We get 'any', losing all TypeScript benefits
const age = Reflect.getMetadata('userAge', Target);
age.toFixed(2); // No IDE hints or type checking
3. No protection against typos in string keys:
// Key typo - will only be discovered at runtime
Reflect.defineMetadata('userInfo', {name: 'John'}, Target);
const data = Reflect.getMetadata('userXnfo', Target); // Typo!
4. Refactoring challenges:
// When changing data structure, you'll have to manually
// find all usage locations
type UserInfo = {
name: string;
age: number;
// Added new field
email: string; // IDE won't suggest where structure needs updating
};
These issues are particularly critical in large projects where metadata is heavily used and maintained by multiple developers.
The Solution
The @e22m4u/ts-reflector library provides an elegant solution through the MetadataKey<T>
class, which adds strict typing for metadata. This allows TypeScript to verify data correctness at compile time and provides complete type information during retrieval. Instead of working with unsafe string keys and any-typed values, we get full typing and IDE support.
When attempting to store incorrect data, TypeScript will immediately flag the error:
import {Reflector, MetadataKey} from '@e22m4u/ts-reflector';
// Define metadata structure
type UserInfo = {
name: string;
age: number;
};
// Create typed key
const USER_INFO = new MetadataKey<UserInfo>();
// TypeScript will highlight the error before compilation
Reflector.defineMetadata(USER_INFO, {
name: 123, // Error: number cannot be assigned to string
age: "30" // Error: string cannot be assigned to number
}, Target);
When retrieving metadata, we no longer work with the any
type - TypeScript knows the exact data structure:
const info = Reflector.getMetadata(USER_INFO, Target); // type: UserInfo
// IDE will provide all string methods
console.log(info.name.toLowerCase());
This is particularly valuable during refactoring - when changing the UserInfo
type, TypeScript will immediately show all places where code needs to be updated. And the IDE hint system makes development more convenient and safer.
Additionally, @e22m4u/ts-reflector is fully compatible with existing reflect-metadata
projects, allowing for gradual implementation while improving type safety step by step.
Top comments (0)