typescript中的元数据及使用场景

在 TypeScript 中,元数据(Metadata) 是指附加到类、方法、属性或参数上的额外信息,这些信息可以在运行时被读取和使用。

元数据通常用于描述目标的结构、行为或其他特性,广泛应用于依赖注入、序列化、验证等场景。

TypeScript 通过 装饰器反射元数据 API 来实现元数据的添加和读取。

1. 什么是元数据?

元数据是"关于数据的数据",它描述了目标(如类、方法、属性等)的额外信息。例如:

  • 一个类的依赖项
  • 一个方法的参数类型
  • 一个属性的验证规则

在 TypeScript 中,元数据通常以键值对的形式存储,可以通过反射 API 在运行时访问。

2. 元数据的使用

TypeScript 提供了 reflect-metadata 库来支持元数据的操作。你需要先安装这个库:

js 复制代码
npm install reflect-metadata

然后在代码中引入:

js 复制代码
import "reflect-metadata";

2.1 添加元数据

使用 Reflect.defineMetadata 方法可以为目标添加元数据。它的语法如下:

js 复制代码
Reflect.defineMetadata(metadataKey, metadataValue, target, propertyKey?);
  • metadataKey:元数据的键(通常是字符串或符号)。
  • metadataValue:元数据的值(可以是任意类型)。
  • target:目标对象(类、方法、属性等)。
  • propertyKey:可选参数,表示属性或方法的名称。

2.2 读取元数据

使用 Reflect.getMetadata 方法可以读取元数据。它的语法如下:

js 复制代码
Reflect.getMetadata(metadataKey, target, propertyKey?);
  • metadataKey:元数据的键。
  • target:目标对象。
  • propertyKey:可选参数,表示属性或方法的名称。

3. 为类、方法、属性和参数添加元数据

3.1 为类添加元数据

js 复制代码
import "reflect-metadata";

# @Reflect.metadata是装饰器写法
@Reflect.metadata("classMeta", "This is a class metadata")
class MyClass {}

const classMeta = Reflect.getMetadata("classMeta", MyClass);
console.log(classMeta); // 输出: This is a class metadata

3.2 为方法添加元数据

js 复制代码
import "reflect-metadata";

class MyClass {
  @Reflect.metadata("methodMeta", "This is a method metadata")
  myMethod() {}
}

const methodMeta = Reflect.getMetadata("methodMeta", MyClass.prototype, "myMethod");
console.log(methodMeta); // 输出: This is a method metadata

3.3 为属性添加元数据

js 复制代码
import "reflect-metadata";

class MyClass {
  @Reflect.metadata("propertyMeta", "This is a property metadata")
  myProperty: string;
}

const propertyMeta = Reflect.getMetadata("propertyMeta", MyClass.prototype, "myProperty");
console.log(propertyMeta); // 输出: This is a property metadata

3.4 为参数添加元数据

js 复制代码
import "reflect-metadata";

class MyClass {
  myMethod(@Reflect.metadata("paramMeta", "This is a parameter metadata") arg1: string) {}
}

const paramMeta = Reflect.getMetadata("paramMeta", MyClass.prototype, "myMethod");
console.log(paramMeta); // 输出: { 0: "This is a parameter metadata" }

4. 元数据的应用场景

4.1 依赖注入

在 NestJS 等框架中,元数据用于实现依赖注入。例如:

js 复制代码
import "reflect-metadata";

function Injectable() {
  return function (target: any) {
    Reflect.defineMetadata("injectable", true, target);
  };
}

@Injectable()
class MyService {}

const isInjectable = Reflect.getMetadata("injectable", MyService);
console.log(isInjectable); // 输出: true

再来看一个例子:

js 复制代码
# 在类的装饰器上面获取类方法的元数据
function controller(target: any) {
  for (let key in target.prototype) {
    console.log('key', Reflect.getMetadata('path', target.prototype, key))
  }
}

# 通过方法装饰器给类的方法设置元数据,即path
function get(path: string) {
  return function (target: any, key: string) {
    Reflect.defineMetadata('path', path, target, key)
  }
}

@controller
class LoginController {
  constructor() {}
  @get('/login')
  login() {}

  @get('/')
  home(req: Request, res: Response) {
    ...
  }
}

4.2 参数验证

可以使用元数据来存储参数的验证规则:

js 复制代码
import "reflect-metadata";

function Validate(min: number, max: number) {
  return function (target: any, propertyKey: string, parameterIndex: number) {
    Reflect.defineMetadata("validation", { min, max }, target, `${propertyKey}_${parameterIndex}`);
  };
}

class MyClass {
  myMethod(@Validate(1, 10) arg1: number) {}
}

const validationRules = Reflect.getMetadata("validation", MyClass.prototype, "myMethod_0");
console.log(validationRules); // 输出: { min: 1, max: 10 }

4.3 序列化

序列化就是把程序中的数据结构(比如对象、数组等)转换为一种可以存储或者传输的格式的过程。这个过程就好像你要把一些物品(数据)装进一个箱子(特定格式)里,方便搬运(存储或传输)。在不同的应用场景中,序列化后的数据格式也有所不同,常见的有 JSON、XML、二进制等。

元数据可以用于描述如何序列化对象:

js 复制代码
import "reflect-metadata";

function Serialize(key: string) {
  return function (target: any, propertyKey: string) {
    Reflect.defineMetadata("serialize", key, target, propertyKey);
  };
}

class MyClass {
  @Serialize("username")
  name: string;
}

const serializationKey = Reflect.getMetadata("serialize", MyClass.prototype, "name");
console.log(serializationKey); // 输出: username

5. 内置元数据

design:type

design:type是元数据的一种内置云数据,用于获取类成员(属性)的类型信息。

js 复制代码
import 'reflect-metadata';

class Example {
  @LogType
  public name: string;

  @LogType
  public age: number;
}

function LogType(target: any, key: string) {
  // 获取属性的类型
  const type = Reflect.getMetadata('design:type', target, key);
  console.log(`${key} 的类型是: ${type.name}`);
}

// 输出:
// name 的类型是: String
// age 的类型是: Number

design:paramtypes / design:returntype

design:paramtypes 获取方法参数的类型(数组形式); design:returntype 获取方法的返回值类型;

js 复制代码
class Example {
  @LogTypes
  public greet(name: string, age: number): boolean {
    return true;
  }
}

function LogTypes(target: any, key: string, descriptor: PropertyDescriptor) {
  const paramTypes = Reflect.getMetadata('design:paramtypes', target, key);
  const returnType = Reflect.getMetadata('design:returntype', target, key);

  console.log(`${key} 的参数类型是:`, paramTypes.map((t: any) => t.name).join(', '));
  console.log(`${key} 的返回值类型是: ${returnType.name}`);
}

// 输出:
// greet 的参数类型是: String, Number
// greet 的返回值类型是: Boolean

6. 总结

  • 元数据 是附加到类、方法、属性或参数上的额外信息,用于描述目标的结构或行为。
  • TypeScript 通过 reflect-metadata 库支持元数据的操作。
  • 使用 Reflect.defineMetadata 添加元数据,使用 Reflect.getMetadata 读取元数据。
  • 元数据的应用场景包括依赖注入、参数验证、序列化等。

通过元数据,你可以编写更灵活、更强大的代码,尤其是在框架开发中非常有用。

相关推荐
打不着的大喇叭41 分钟前
uniapp的光标跟随和打字机效果
前端·javascript·uni-app
无我Code1 小时前
2025----前端个人年中总结
前端·年终总结·创业
程序猿阿伟1 小时前
《前端路由重构:解锁多语言交互的底层逻辑》
前端·重构
Sun_light1 小时前
6个你必须掌握的「React Hooks」实用技巧✨
前端·javascript·react.js
爱学习的茄子1 小时前
深度解析JavaScript中的call方法实现:从原理到手写实现的完整指南
前端·javascript·面试
莫空00001 小时前
Vue组件通信方式详解
前端·面试
呆呆的心1 小时前
揭秘 CSS 伪元素:不用加标签也能玩转出花的界面技巧 ✨
前端·css·html
susnm1 小时前
Dioxus 与数据库协作
前端·rust
优雅永不过时_v1 小时前
基于vite适用于 vue和 react 的Three.js低代码与Ai结合编辑器
前端·javascript
小皮侠1 小时前
nginx的使用
java·运维·服务器·前端·git·nginx·github