🔥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);
        }
    })
相关推荐
We་ct27 分钟前
LeetCode 226. 翻转二叉树:两种解法(递归+迭代)详解
前端·算法·leetcode·链表·typescript
哆啦A梦158832 分钟前
Vue3魔法手册 作者 张天禹 013_pinia
前端·vue.js·typescript
哆啦A梦158834 分钟前
Vue3魔法手册 作者 张天禹 014_组件通信
前端·vue.js·typescript
码云数智-园园1 小时前
Vue 3 + TypeScript 企业级项目架构实战:从0到1打造可维护的前端工程体系
前端·vue.js·typescript
We་ct4 小时前
LeetCode 101. 对称二叉树:两种解法(递归+迭代)详解
前端·算法·leetcode·链表·typescript
哆啦A梦15886 小时前
Vue3魔法手册 作者 张天禹 015_插槽
前端·vue.js·typescript·vue3
我不吃饼干15 小时前
TypeScript 类型体操练习笔记(二)
前端·typescript
NEXT0617 小时前
深度解析 JWT:从 RFC 原理到 NestJS 实战与架构权衡
前端·typescript·nestjs
程序员林北北18 小时前
【前端进阶之旅】节流与防抖:前端性能优化的“安全带”与“稳定器”
前端·javascript·vue.js·react.js·typescript
coding随想1 天前
ESM + TypeScript:零配置实现类型安全的现代开发
安全·ubuntu·typescript