🔥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);
        }
    })
相关推荐
程序视点4 小时前
【Vue3新工具】Pinia.js:提升开发效率,更轻量、更高效的状态管理方案!
前端·javascript·vue.js·typescript·vue·ecmascript
周三有雨9 小时前
【面试题系列Vue07】Vuex是什么?使用Vuex的好处有哪些?
前端·vue.js·面试·typescript
小王码农记15 小时前
vue中路由缓存
前端·vue.js·缓存·typescript·anti-design-vue
Star7682 天前
ts泛型的一个小知识
前端·typescript
盛夏绽放2 天前
Vue 3与TypeScript集成指南:构建类型安全的前端应用
前端·vue.js·typescript
Swift社区2 天前
使用 AI 在医疗影像分析中的应用探索
typescript·tensorflow·openai
九月儿3 天前
Vue3 + Vite 项目引入 Typescript
前端·typescript
周三有雨3 天前
Object.prototype.hasOwnProperty.call(item, key) 作用与用途
前端·javascript·vue.js·typescript·原型模式
swipe3 天前
Typescript进阶之类型体操套路四
前端·javascript·typescript
MavenTalk4 天前
Solana应用开发常见技术栈
rust·typescript·公链·solana·区块链开发·web3.js