🔥TS进阶之「装饰器」

装饰器

概述

面向对象的概念(java:注解,c#:特征),decorator

angular大量使用,react中也会用到

目前JS支持装饰器,目前处于建议征集的第三阶段

解决的问题

装饰器,能够带来额外的信息量,可以达到分离关注点的目的。

  • 信息书写位置的问题
  • 重复代码的问题

上述两个问题产生的根源:某些信息,在定义时,能够附加的信息量有限。

装饰器的作用:为某些属性、类、参数、方法提供元数据信息(metadata)

元数据:描述数据的数据

装饰器的本质

在JS中,装饰器是一个函数。(装饰器是要参与运行的)

装饰器可以修饰:

  • 成员(属性+方法)
  • 参数

类装饰器

类装饰器的本质是一个函数,该函数接收一个参数,表示类本身(构造函数本身)

使用装饰器@得到一个函数

在TS中,如何约束一个变量为类

  • Function
  • new (参数)=>object
ts 复制代码
// 第一种:
function test(target: Function){
    console.log(target)
}

// 第二种:
function test(target: new() => object){
    console.log(target)
}

@test
class A{}

运行结果:

装饰器函数的运行时间:在类定义后直接运行

在TS中要使用装饰器,需要开启experimentalDecorators

类装饰器可以具有的返回值:

  • void:仅运行函数
  • 返回一个新的类:会将新的类替换掉装饰目标
ts 复制代码
type constructor = new (...args: any[]) => object;

function d1(){
  console.log("d1")
  return function(target: constructor){
    console.log('d1 decorator')
  }
}
function d2(){
  console.log("d2")
  return function(target: constructor){
    console.log('d2 decorator')
  }
}

@d1()
@d2()
class A {
  num: number
}
const a = new A()

运行结果:

可以有多个装饰器修饰

成员装饰器

属性:

属性装饰器也是一个函数,该函数需要两个参数:

  1. 如果是静态属性,则为类本身;如果是实例属性,则为类的原型;
  2. 固定为一个字符串,表示属性名
ts 复制代码
function d(target: any, key: string){
  console.log(target, key)
}

class A {
  @d
  static prop1: string

  @d
  prop2: number
}

方法:

方法装饰器也是一个函数,该函数需要三个参数:

  1. 如果是静态方法,则为类本身;如果是实例方法,则为类的原型;
  2. 固定为一个字符串,表示方法名
  3. 属性描述对象
ts 复制代码
function enumerable(target: any, key: string, descriptor: PropertyDescriptor) {
    // console.log(target, key, descriptor);
    descriptor.enumerable = true;
}

function useless(target: any, key: string, descriptor: PropertyDescriptor) {
    descriptor.value = function () {
        console.warn(key + "方法已过期");
    }
}

class A{
  @enumerable
  @useless
  method1() {
    console.log("method1")
  }

  @enumerable
  method2() {
    console.log("method2")
  }
}
const a = new A()

可以有多个装饰器修饰

reflect-metadata库

该库的作用:保存元数据

封装一个工具函数:

ts 复制代码
const key = Symbol.for("descriptor");

export function descriptor(description: string) {
  return Reflect.metadata(key, description);
}

// 提供打印的功能
export function printObj(obj: any) {
  const cons = Object.getPrototypeOf(obj);
  if (Reflect.hasMetadata(key, cons)) {
    console.log(Reflect.getMetadata(key, cons));
  } else {
    console.log(cons.constructor.name);
  }

  // 输出所有的属性描述和属性值
  for (const k in obj) {
    if (Reflect.hasMetadata(key, obj, k)) {
      console.log(`\t${Reflect.getMetadata(key, obj, k)}:${obj[k]}`)
    }
    else{
      console.log(`\t${k}:${obj[k]}`)
    }
  }
}

页面使用:

ts 复制代码
import { descriptor } from './Descriptor'
import "reflect-metadata";

@Reflect.metadata("a", "一个类")
@Reflect.metadata("a1", "一个类A1")
@Reflect.metadata("a2", "一个类A2")

class A{
  @Reflect.metadata('prop', "一个属性")
  prop1: string
}

const obj = new A()
console.log(Reflect.getMetadata('a', A))
console.log(Reflect.getMetadata('a', Object.getPrototypeOf(obj).constructor))
console.log(Reflect.getMetadata('prop', obj,'prop1' ))

class-validator 库

ts 复制代码
import {IsNotEmpty, MinLength,MaxLength ,Min, Max,validate} from 'class-validator'
class User{
  @IsNotEmpty({message: '手机号不能为空'})
  @MinLength(5, { message: '手机号必须至少有5个字符'})
  @MaxLength(11, { message: '手机号不能超过11个字符'})
  phone: string
  password: string

  @IsNotEmpty({message: '年龄不能为空'})
  @Min(0,{message: '年龄最小值为0'})
  @Max(100,{message: '年龄最大值为100'})
  age: number
}
const user = new User()
user.phone = '15282088586888'
validate(user).then(errors =>{
  console.log(errors)
})

class-transformer 库

ts 复制代码
import { plainToClass, Type } from "class-transformer"
import axios from "axios"

class User {
    id: number
    firstName: string
    lastName: string
    
    @Type(() => Number)
    age: number

    getName() {
        return this.firstName + " " + this.lastName;
    }

    isAdult() {
        return this.age > 36 && this.age < 60;
    }
}

axios.get("https://api.myjson.com/bins/1b59tw").then(resp => resp.data)
    .then(users => {
        const us = plainToClass(User, users);
        for (const u of us) {
            console.log(typeof u.age, u.age);
        }
    })
相关推荐
ttod_qzstudio7 小时前
解决 Vue 3 + TypeScript 中 v-for 循环类型推断问题
前端·vue.js·typescript
今天头发还在吗19 小时前
【React】动态SVG连接线实现:图片与按钮的可视化映射
前端·javascript·react.js·typescript·前端框架
冷冷的菜哥21 小时前
react多文件分片上传——支持拖拽与进度展示
前端·react.js·typescript·多文件上传·分片上传
Kisang.1 天前
【HarmonyOS】窗口管理实战指南
前端·华为·typescript·harmonyos·鸿蒙
Dajiaonew2 天前
Vue3 + TypeScript 一篇文章 后端变全栈
前端·javascript·typescript
敲敲敲敲暴你脑袋2 天前
用3Dmol.js展示3D分子结构
typescript·webgl·数据可视化
还是大剑师兰特2 天前
TypeScript 面试题及详细答案 100题 (11-20)-- 基础类型与类型操作
typescript·大剑师·typescript教程·typescript面试题
用户47949283569152 天前
TypeScript 和 JavaScript 的 'use strict' 有啥不同
前端·javascript·typescript
用户47949283569152 天前
还不知道'use strict'的作用?这篇文章给你讲清楚
前端·javascript·typescript
乐影3 天前
TS 模板字符串类型:从基础到进阶的类型编程魔法
前端·typescript