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

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

相关推荐
foxhuli22933 分钟前
禁止ifrmare标签上的文件,实现自动下载功能,并且隐藏工具栏
前端
青皮桔1 小时前
CSS实现百分比水柱图
前端·css
影子信息1 小时前
vue 前端动态导入文件 import.meta.glob
前端·javascript·vue.js
青阳流月1 小时前
1.vue权衡的艺术
前端·vue.js·开源
RunsenLIu1 小时前
基于Vue.js + Node.js + MySQL实现的图书销售管理系统
vue.js·mysql·node.js
样子20181 小时前
Vue3 之dialog弹框简单制作
前端·javascript·vue.js·前端框架·ecmascript
kevin_水滴石穿1 小时前
Vue 中报错 TypeError: crypto$2.getRandomValues is not a function
前端·javascript·vue.js
孤水寒月2 小时前
给自己网站增加一个免费的AI助手,纯HTML
前端·人工智能·html
CoderLiu2 小时前
用这个MCP,只给大模型一个figma链接就能直接导出图片,还能自动压缩上传?
前端·llm·mcp
伍哥的传说2 小时前
鸿蒙系统(HarmonyOS)应用开发之实现电子签名效果
开发语言·前端·华为·harmonyos·鸿蒙·鸿蒙系统