前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >TypeScript系列教程十一《装饰器》 -- reflect-metadata

TypeScript系列教程十一《装饰器》 -- reflect-metadata

作者头像
星宇大前端
发布2022-05-06 17:20:20
1.9K0
发布2022-05-06 17:20:20
举报
文章被收录于专栏:大宇笔记

reflect-metadata 是ES7 的提案 ,TypeScript 1.5 已经开始使用。reflect-metadata是一个单独的npm 包,具体介绍可以看看官方介绍。

系列教程

reflect-metadata 拆成两个单词,reflect 反射和 metadata,通俗理解 利用反射的原理修改元数据。

元数据就是配置数据的数据,reflect-metadata 利用反射的原理通过key、value的形式给对象、对象属性设置数据,从而不改变其数据结构。

安装配置

首先我们需要单独引入这个包:

代码语言:javascript
复制
npm install reflect-metadata

在tsconfig里打开下面属性支持装饰器和元数据:

代码语言:javascript
复制
  /* Enables experimental support for ES7 decorators.*/
  "experimentalDecorators": true, 
 /* Enables experimental support for emitting type metadata for decorators. */
  "emitDecoratorMetadata": true,  

API

反射元数据可以在对象或者对象属性上添加元数据,提供装饰器在类的原型对象和对象属性上添加元数据。

代码语言:javascript
复制
//在对象或属性上定义源数据
Reflect.defineMetadata(metadataKey, metadataValue, target);
Reflect.defineMetadata(metadataKey, metadataValue, target, propertyKey);

// 检查某个源数据的 key 是否存在某个对象或属性上
let result = Reflect.hasMetadata(metadataKey, target);
let result = Reflect.hasMetadata(metadataKey, target, propertyKey);

// 检查是否有自带源数据 key 存在某个对象或属性上
let result = Reflect.hasOwnMetadata(metadataKey, target);
let result = Reflect.hasOwnMetadata(metadataKey, target, propertyKey);

// 通过 key 在对象或属性的原型链上获取源数据的值
let result = Reflect.getMetadata(metadataKey, target);
let result = Reflect.getMetadata(metadataKey, target, propertyKey);

// 通过 自带源数据 key 在对象或属性的原型链上获取源数据的值
let result = Reflect.getOwnMetadata(metadataKey, target);
let result = Reflect.getOwnMetadata(metadataKey, target, propertyKey);

// 获取对象或属性原型链上的所有源数据
let result = Reflect.getMetadataKeys(target);
let result = Reflect.getMetadataKeys(target, propertyKey);

// 获取对象或属性上所有自带的源数据 keys
let result = Reflect.getOwnMetadataKeys(target);
let result = Reflect.getOwnMetadataKeys(target, propertyKey);

// 在对象或属性上删除源数据
let result = Reflect.deleteMetadata(metadataKey, target);
let result = Reflect.deleteMetadata(metadataKey, target, propertyKey);

//通过修饰器在构造函数上应用源数据
@Reflect.metadata(metadataKey, metadataValue)
class C {

  // 通过修饰器在方法或属性上应用源数据
  @Reflect.metadata(metadataKey, metadataValue)
  method() {
  }
}

我们已经在 tsconfig.json 中开启了 emitDecoratorMetadata 选项,此时,TypeScript 在编译时定义一些 元数据设计键,目前可用的有:

  • 属性类型元数据 design:type :用于获取类属性的类型
  • 参数类型元数据 design:paramtypes:用于获取方法参数的类型
  • 返回类型元数据 design:returntype:用于获取返回值的类型

目前只有这三个设计键可用,但已经足够覆盖大部分常见场景了。

说了那么多概念,那么这个reflect-matedata 有什么用呢?有哪些使用场景?

使用场景

reflect-matedata 光看api很容易明白,使用也简单,主要是思想和使用场景比较抽象,在什么时候可以使用到他呢?下面总结了几个。

参数统一处理

主要是想统一替换实例,拦截然后重新插入。

示例主要演示了,不论接收什么参数,都可以重新拦截修改注入。

  • 方法装饰器
  • 根据 reflect-matedata design:paramtypes 拿到方法参数类型
  • 根据类型实例化修改然后重新注入

代码:

无论我传男生还是女生,我都统一拦截处理修改成了中性。

代码语言:javascript
复制
import "reflect-metadata";

class People {
  sex:string
} 

class Women implements People{

  sex: string = '女生'
}

class Man implements People{

  sex: string = '男生'
}


function SexDecorate(target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor){
  let PropsTypes = Reflect.getMetadata('design:paramtypes',target,propertyKey)
  let p =   new  PropsTypes[0]()
  p.sex = '中性'
  let orgMethod = descriptor.value 
  descriptor.value = ()=>{
    orgMethod(p)
  }
}

class Student {
 
  
  @SexDecorate
  getSex(obj:People){
    console.log(obj);
    
  }
  
}

let man = new Man()
let women = new Women()

let std = new Student()
std.getSex(man)
std.getSex(women)

打印结果:

控制反转和依赖注入

在Angular 和nestjs 中有大量的注入,这也是我从新学习装饰器的目的,就是看懂nestjs 代码。

说这个之前,先做个试验:

代码语言:javascript
复制
class Tes {
    constructor(a:string) {
    }
    run(name:string){
    }
}
console.log(Reflect.getMetadata('design:paramtypes', Tes));

我要通过 Reflect.getMetadata 去构造函数参数,这样是取不出来的,必须加上装饰器。

我加一个装饰器就可以取出来了:

代码语言:javascript
复制
const a:()=>ClassDecorator = ()=>{
    return (target:Function)=>{

    }
}

@a()
class Tes {
    constructor(a:string) {
        
    }
    run(name:string){

    }
}

  console.log(Reflect.getMetadata('design:paramtypes', Tes));

下面看一段简单的注入实现代码:

代码语言:javascript
复制
import "reflect-metadata";
// 构造函数类型,注入的依赖必须是可以按照这个构造函数构造的。
type Constructor<T = any> = new (...args: any[]) => T;
// 注入依赖装饰器,为了能取到元数据参数。
const Injectable = (): ClassDecorator => target => {};

class OtherService {
  a = 1;
}

@Injectable()
class TestService {
  constructor(public readonly otherService: OtherService) {}

  testMethod() {
    console.log(this.otherService.a);
  }
}


const Factory = <T>(target: Constructor<T>): T => {
    // 获取所有注入的服务
    const providers = Reflect.getMetadata('design:paramtypes', target); // [OtherService]
    console.log(providers);
     // 根据构造函数类型实例化
    const args = providers.map((provider: Constructor) => new provider());
    //构造服务并将构造依赖示例参数传入
    return new target(...args);
  };
  
  Factory(TestService).testMethod(); // 1

到此结束,下面关于参数和属性装饰器示例还要用到reflect-metadata。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-04-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 系列教程
    • 安装配置
      • API
        • 使用场景
          • 参数统一处理
        • 控制反转和依赖注入
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档