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

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

相关推荐
朝阳5812 分钟前
在一台服务器上通过 Nginx 配置实现不同子域名访问静态文件和后端服务
服务器·前端·nginx
水银嘻嘻19 分钟前
web 自动化之 Selenium 元素定位和浏览器操作
前端·selenium·自动化
GanGuaGua44 分钟前
CSS:元素显示模式与背景
前端·javascript·css·html
一个会的不多的人1 小时前
C# NX二次开发:判断两个体是否干涉和获取系统日志的UFUN函数
前端·javascript·html
小离a_a1 小时前
uniapp tabBar 中设置“custom“: true 在H5和app中无效解决办法
前端·uni-app
travel_wsy1 小时前
webrtc 视频直播
前端·vue.js·音视频·webrtc
zybsjn2 小时前
开发 Chrome 扩展中的侧边栏图标设置实录(Manifest V3)
前端·chrome
weixin_428498492 小时前
在Star-CCM+中实现UDF并引用场数据和网格数据
java·前端
layman05282 小时前
node.js 实战——express图片保存到本地或服务器(七牛云、腾讯云、阿里云)
node.js·express
小猫猫改bug3 小时前
threejs 添加css3d标签 vue3
前端·javascript·css3