🔥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);
        }
    })
相关推荐
@逆风微笑代码狗9 小时前
148.《mobx-react-lite + TypeScript 入门实战教程(完整版)》
前端·react.js·typescript
炽烈小老头11 小时前
函数式编程范式(二)
前端·typescript
三掌柜66614 小时前
TypeScript+React 全栈生态实战:从架构选型到工程落地,告别开发踩坑
react.js·架构·typescript
siger1 天前
花式玩转TypeScript类型-我使用swagger的描述文件自动生成类型的npm包供前端使用
前端·typescript·npm
We་ct1 天前
LeetCode 22. 括号生成:DFS回溯解法详解
前端·数据结构·算法·leetcode·typescript·深度优先·回溯
用户5757303346241 天前
🎬 从「写崩三个项目」到「TypeScript 真香」:一个 Todo 列表的血泪救赎史
typescript
向上的车轮1 天前
TypeScript 一日速通指南:数据类型全解析与转换指南
javascript·typescript
Bling_Bling_11 天前
TypeScript 核心知识点整理
typescript
我命由我123452 天前
Element Plus - Cascader 观察记录(基本使用、动态加载、动态加载下的异常环境)
开发语言·前端·javascript·vue.js·typescript·html5·js
xiaoxue..2 天前
前后端双令牌认证(Access Token + Refresh Token)全方案实现:安全与体验兼得
前端·后端·web安全·面试·typescript·nestjs