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 读取元数据。
  • 元数据的应用场景包括依赖注入、参数验证、序列化等。

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

相关推荐
luckyext10 分钟前
Postman发送GET请求示例及注意事项
前端·后端·物联网·测试工具·小程序·c#·postman
小满zs30 分钟前
React第三十章(css原子化)
前端·react.js
一直在学习的小白~1 小时前
前端项目中创建自动化部署脚本,用于 Jenkins 触发 npm run publish 来完成远程部署
前端·自动化·jenkins
Perfect—完美1 小时前
Vue 3 事件总线详解:构建组件间高效通信的桥梁
前端·javascript·vue.js
二川bro1 小时前
模拟类似 DeepSeek 的对话
前端·人工智能
祈澈菇凉2 小时前
Vue 中如何实现自定义指令?
前端·javascript·vue.js
sorryhc2 小时前
解读Ant Design X API流式响应和流式渲染的原理
前端·react.js·ai 编程
1024小神2 小时前
vue/react前端项目打包的时候加上时间,防止后端扯皮
前端·vue.js·react.js
拉不动的猪2 小时前
刷刷题35(uniapp中级实际项目问题-2)
前端·javascript·面试
bigcarp2 小时前
理解langchain langgraph 官方文档示例代码中的MemorySaver
java·前端·langchain