凶凶 發表於 2022-4-11 18:01:00

TypeScript装饰器Decorators学习

<p></p><div class="toc"><div class="toc-container-header">目录</div><ul><li>TypeScript装饰器Decorators学习<ul><li>装饰器与继承的区别</li><li>配置TS装饰器环境</li><li>类装饰器decorator的基本使用</li><li>装饰器decorator语法糖</li><li>ts装饰器叠加</li><li>通过TS装饰器实现统一消息回应</li><li>装饰器工厂在TS中的使用</li><li>方法装饰器</li><li>静态方法装饰器与writable</li><li>使用装饰器实现文本高亮</li><li>延迟执行在装饰器中的实现</li><li>使用装饰器工厂控制延迟时间</li><li>装饰器全局异常管理</li><li>装饰器工厂自定义 <code>console.log</code></li><li>用户登录验证在TS装饰器中的实现</li><li>数据权限控制访问方法</li><li>使用装饰器模拟超快速的网络请求</li><li>属性修饰器和参数修饰器</li><li>属性访问器动态转换对象属性</li><li>使用ts的属性装饰器创建随机色块</li><li>元数据<code>reflect-metadata</code>的使用<ul><li><code>Reflect.getMetadata</code></li><li><code>Reflect.defineMetadata</code></li><li><code>Reflect.metadata</code></li></ul></li><li>使用Reflect-metadata的defineMetadata和getMetadata配置验证数据</li></ul></li></ul></div><p></p>
<h1 id="typescript装饰器decorators学习">TypeScript装饰器Decorators学习</h1>
<h2 id="装饰器与继承的区别">装饰器与继承的区别</h2>
<p>装饰器可以给代码提供功能。<br>
现实生活当中张三想有一辆车,他可以通过继承的方式让父亲给他一辆车。<br>
装饰器是现在比如说张三有一辆车,他想换个内饰,方向盘,或者轮骨这样的。<br>
继承是在父子类之间进行的,父类有的功能子类可以拿来用,当然有的情况下父类设置为private子类就无法使用了,装饰器可以对功能进行装饰,装饰类的方法、属性甚至整个类等等。<br>
装饰器更加的灵活,继承的话就是拿来就用。</p>
<h2 id="配置ts装饰器环境">配置TS装饰器环境</h2>
<p>需要我们有<code>tsconfig.json</code>的配置文件。</p>
<p>终端命令:<code>tsc --init</code></p>
<p>然后我们需要将装饰器所需要的配置项打开:</p>
<pre><code class="language-json">{
"compilerOptions": {
        "target": "es2016",
    "experimentalDecorators": true,
        "emitDecoratorMetadata": true,
    ......
}
}
</code></pre>
<p>然后是两个比较常用的终端命令:</p>
<pre><code>tsc 1.ts -w // 监视某一个ts文件
tsc -w // 根据配置文件监视整个项目的文件
</code></pre>
<p>当然也可以在菜单栏使用终端-运行任务-typescript-监视来实现对整个项目文件的监视。</p>
<h2 id="类装饰器decorator的基本使用">类装饰器decorator的基本使用</h2>
<p>可以在原型链上无限增加内容,让其拥有新的属性啊,方法呀什么的。像vue中的混入(mixin),装饰器就是这样的特性,开放封闭原则。</p>
<pre><code class="language-tsx">const moveDecorator: ClassDecorator = (target: Function) =&gt; {
    target.prototype.name = 'bleak'
    target.prototype.getPosition = () : {x: number, y: number} =&gt; {
      return {x: 100, y: 200}
    }
}

@moveDecorator
class Tank {
    // public getPosition() {}
}

const t = new Tank()
console.log((t as any).getPosition()) // { x: 100, y: 200 }
console.log((&lt;any&gt;t).getPosition()) // { x: 100, y: 200 }
console.log((t as any).name) // bleak

@moveDecorator
class Player {
    public getPosition() {}
}

const p = new Player()
console.log(p.getPosition()) // { x: 100, y: 200 }
</code></pre>
<h2 id="装饰器decorator语法糖">装饰器decorator语法糖</h2>
<p>@符号的方式就是语法糖的表现形式, 我们在实例化对象new Function来创建对象, 为了与其他语言相似, ES6推出了class类的概念, 其实class内部还是通过构造函数的方式来进行操作, 归根到底还是原型的概念.</p>
<pre><code class="language-tsx">const moveDecorator: ClassDecorator = (target: Function) =&gt; {
    target.prototype.name = 'bleak'
    target.prototype.getPosition = () : {x: number, y: number} =&gt; {
      return {x: 100, y: 200}
    }
}

// @moveDecorator
class Tank {}
moveDecorator(Tank)
const t = new Tank()
console.log((&lt;any&gt;t).getPosition()) // { x: 100, y: 200 }
</code></pre>
<p>我们会发现我们不使用装饰器@语法的情况下, 直接使用该函数传入类与使用装饰器的效果相同, 只是装饰器是自动的帮我们执行了一下, 不用我们再去写一行代码去执行.</p>
<h2 id="ts装饰器叠加">ts装饰器叠加</h2>
<p>在ts中装饰器是可以叠加的,比如我们可以像如下代码一样叠加多个类装饰器:</p>
<pre><code class="language-tsx">const moveDecorator: ClassDecorator = (target: Function) =&gt; {
    target.prototype.name = 'bleak'
    target.prototype.getPosition = () : {x: number, y: number} =&gt; {
      return {x: 100, y: 200}
    }
}

const MusicDecorator: ClassDecorator = (target: Function) =&gt; {
    target.prototype.playMusic = (): void =&gt; {
      console.log('播放音乐')
    }
}


@moveDecorator
@MusicDecorator
class Tank {}

const t = new Tank()
console.log((t as any).getPosition()); // { x: 100, y: 200 }
(&lt;any&gt;t).playMusic() // 播放音乐
</code></pre>
<p>我们可以通过使用多个装饰器来给类添加多个不同的功能,比如上面添加的一个是获取位置和名字的功能,一个是播放音乐的功能。</p>
<h2 id="通过ts装饰器实现统一消息回应">通过TS装饰器实现统一消息回应</h2>
<p>我们可以通过装饰器给多个类添加统一的功能,当然我们也可以通过继承来实现。</p>
<pre><code class="language-tsx">const MessageDecorator:ClassDecorator = (target:Function) =&gt; {
    target.prototype.message = (content: string) =&gt; {
      console.log(content)
    }
}

@MessageDecorator
class LoginController {
    public login() {
      console.log("登录业务处理")
      ;(this as any).message("恭喜你,登录成功了")
    }
}

new LoginController().login() // 登录业务处理 恭喜你,登录成功了

@MessageDecorator
class ArticleController {
    public store() {
      (this as any).message("文章添加成功")
    }
}
new ArticleController().store() // 文章添加成功
</code></pre>
<h2 id="装饰器工厂在ts中的使用">装饰器工厂在TS中的使用</h2>
<p>我们可以根据需要使用装饰器工厂返回不同的装饰器, 根据传入参数的不同,我们可以返回不同的装饰器,虽然我们下面只是根据一个参数,但是我们也用多个参数来区分要返回的装饰器。</p>
<pre><code class="language-tsx">const MusicDecoratorFactory = (type: string): ClassDecorator =&gt; {
    switch(type) {
      case 'Tank':
            return (target:Function) =&gt; {
                target.prototype.playMusic = (): void =&gt; {
                  console.log('播放战争音乐')
                }
            }
      default:
            return (target:Function) =&gt; {
                target.prototype.playMusic = (): void =&gt; {
                  console.log('播放电音')
                }
            }
    }
   
}

@MusicDecoratorFactory('Tank')
class Tank {}

const t = new Tank()
;(&lt;any&gt;t).playMusic() // 播放战争音乐


@MusicDecoratorFactory('Player')
class Player {}

(new Player() as any).playMusic() // 播放电音
</code></pre>
<p>当然,方法装饰器呀,属性装饰器呀等装饰器同样可以使用装饰器工厂。</p>
<h2 id="方法装饰器">方法装饰器</h2>
<pre><code class="language-tsx">const showDecorator:MethodDecorator = (target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor): PropertyDescriptor | void =&gt; {
    console.log(target)
    console.log(propertyKey)
    console.log(descriptor)
    target.name = 'bleak'
}

class User {
    @showDecorator
    public show() {
      console.log("It's my show time")
    }
}

console.log((new User() as any).name)
/* 结果
{}
show
{
value: ,
writable: true,
enumerable: false,
configurable: true
}
bleak
*/
</code></pre>
<ul>
<li>方法装饰器的第一个参数target,如果我们是给静态方法添加的方法装饰器,那么target就是构造函数,如果是普通方法,那么target就是原型对象。</li>
<li>方法装饰器的第二个参数propertyKey,是我们方法的名称</li>
<li>方法装饰器的第三个参数descriptor,是对方法属性的描述,包括其函数体的具体内容value,其可写性writable,可枚举性(迭代性)enumerable和可配置性configurable.<br>
我们可以像如下方式一样修改方法体:</li>
</ul>
<pre><code class="language-tsx">const showDecorator:MethodDecorator = (target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor): PropertyDescriptor | void =&gt; {
    console.log(target)
    console.log(propertyKey)
    console.log(descriptor);
    descriptor.value = () =&gt; {
      console.log("Now it's bleak's show time")
    }
}

class User {
    @showDecorator
    public show() {
      console.log("It's my show time")
    }
}

new User().show() // Now it's bleak's show time
</code></pre>
<h2 id="静态方法装饰器与writable">静态方法装饰器与writable</h2>
<pre><code class="language-tsx">const showDecorator:MethodDecorator = (target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor): PropertyDescriptor | void =&gt; {
    descriptor.value = () =&gt; {
      console.log("Now it's bleak's show time")
    }
}

class User {
    @showDecorator
    public static show() {
      console.log("It's my show time")
    }
}

User.show() // Now it's bleak's show time
</code></pre>
<p>无论是静态方法还是普通方法,调用装饰器的时候第三个参数都是对方法属性的描述。<br>
如果我们把writable设置为false,那么我们就无法再对方法进行重写。</p>
<pre><code class="language-tsx">const showDecorator:MethodDecorator = (target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor): PropertyDescriptor | void =&gt; {

    descriptor.writable = false
}

class User {
    @showDecorator
    public static show() {
      console.log("It's my show time")
    }
}

User.show() // Now it's bleak's show time
// × Error
User.show = () =&gt; {
    console.log('show method changed.')
}
</code></pre>
<h2 id="使用装饰器实现文本高亮">使用装饰器实现文本高亮</h2>
<pre><code class="language-tsx">const highlightDecorator:MethodDecorator = (target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor): PropertyDescriptor | void =&gt; {
    const method = descriptor.value
    descriptor.value = () =&gt; {
      return `&lt;div style='color:red;'&gt;${method()}&lt;/div&gt;`
    }
}

class User {
    @highlightDecorator
    public static show() {
      return "It's my show time"
    }
}


console.log(User.show()) // &lt;div style='color:red;'&gt;It's my show time&lt;/div&gt;
</code></pre>
<ol>
<li>首先把原来的方法保存</li>
<li>重新写一个新的方法</li>
<li>在新的方法中使用原来的方法,即可实现文字的高亮效果。</li>
</ol>
<h2 id="延迟执行在装饰器中的实现">延迟执行在装饰器中的实现</h2>
<pre><code class="language-tsx">const SleepDecorator:MethodDecorator = (target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor): PropertyDescriptor | void =&gt; {
    const method = descriptor.value
    descriptor.value = () =&gt; {
      setTimeout(() =&gt; {
            method()
      }, 2000)
    }
}

class User {
    @SleepDecorator
    public show() {
      console.log("It's my show time")
    }
}
new User().show()
</code></pre>
<h2 id="使用装饰器工厂控制延迟时间">使用装饰器工厂控制延迟时间</h2>
<pre><code class="language-tsx">const SleepDecoratorFactory =
    (times: number):MethodDecorator =&gt;
    (...args: any[]) =&gt; {
      const [ , ,descriptor] = args
      const method = descriptor.value
      descriptor.value = () =&gt; {
            setTimeout(() =&gt; {
                method()
            }, times)
      }
    }

class User {
    @SleepDecoratorFactory(500)
    public show() {
      console.log("It's my show time")
    }
}
new User().show()
</code></pre>
<h2 id="装饰器全局异常管理">装饰器全局异常管理</h2>
<pre><code class="language-tsx">const ErrorDecorator:MethodDecorator = (target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) =&gt; {
    const method = descriptor.value
    descriptor.value = () =&gt; {
      try {
            method()
      } catch(error: any) {
            console.log(`%cbleak发现错误了`, 'color:green;font-size:30px;')
            console.log(`%c${error.message}`, 'color:red; font-size:16px')
      }
    }

}

class User {
    @ErrorDecorator
    find() {
      throw new Error("您查找的用户不存在")
    }

    @ErrorDecorator
    create() {
      throw new Error("创建用户失败")
    }
}

new User().create()
</code></pre>
<p><img src="https://img2022.cnblogs.com/blog/2550942/202204/2550942-20220410170340573-1812498372.png"></p>
<h2 id="装饰器工厂自定义-consolelog">装饰器工厂自定义 <code>console.log</code></h2>
<p>我们可以通过装饰器工厂来实现打印错误时自定义消息内容:</p>
<pre><code class="language-tsx">const ErrorDecoratorFactory = (title: string='bleak发现错误了', titleFontSize: number = 20):MethodDecorator =&gt; {
    return (target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) =&gt; {
      const method = descriptor.value
      descriptor.value = () =&gt; {
            try {
                method()
            } catch(error: any) {
                console.log(`%c${title}`, `color:green;font-size:${titleFontSize}px;`)
                console.log(`%c${error.message}`, 'color:red; font-size:16px;')
            }
      }
   
    }
}

class User {
    @ErrorDecoratorFactory()
    find() {
      throw new Error("您查找的用户不存在")
    }

    @ErrorDecoratorFactory('Bleak Find Error https://www.cnblogs.com/bleaka/', 12)
    create() {
      throw new Error("创建用户失败")
    }
}

new User().create()
</code></pre>
<p><img src="https://img2022.cnblogs.com/blog/2550942/202204/2550942-20220410172248210-1402156637.png"></p>
<h2 id="用户登录验证在ts装饰器中的实现">用户登录验证在TS装饰器中的实现</h2>
<pre><code class="language-tsx">const user = {
    name: 'Bleak',
    isLogin: false
}

const AccessDecorator: MethodDecorator =(target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) =&gt; {
    const method = descriptor.value
    descriptor.value = () =&gt; {
      if(user.isLogin === true) {
            return method()
      } else {
            alert('请登陆后操作')
            location.href = 'login.html'
      }

    }
}

class Article {
    show() {
      console.log('显示文章')
    }
    @AccessDecorator
    store() {
      console.log('保存文章')
    }
}

new Article().store() // 跳转到登录页面
</code></pre>
<h2 id="数据权限控制访问方法">数据权限控制访问方法</h2>
<pre><code class="language-tsx">type userType = {
    name: string,
    isLogin: boolean,
    permissions: string[]
}
const user:userType = {
    name: 'Bleak',
    isLogin: true,
    permissions: ["store"]
}

const AccessDecoratorFactory = (keys: string[]): MethodDecorator =&gt; {
    return (target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) =&gt; {
      const method = descriptor.value

      // 定义一个方法来检测有效性
      const validate = () =&gt;
            keys.every (k =&gt; {
                return user.permissions.includes(k)
            })

      descriptor.value = () =&gt; {
            if(user.isLogin === true &amp;&amp; validate()) {
                alert('验证通过')
                method()
            } else {
                alert('验证失败')
            }
   
      }
    }
}

class Article {
    show() {
      console.log('显示文章')
    }
    @AccessDecoratorFactory(['store'])
    store() {
      console.log('保存文章')
    }
}

new Article().store()
</code></pre>
<h2 id="使用装饰器模拟超快速的网络请求">使用装饰器模拟超快速的网络请求</h2>
<pre><code class="language-tsx">const RequestDecorator = (url: string): MethodDecorator =&gt; {
    return (target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) =&gt; {
      const method = descriptor.value
      // axios.get(url).then()
      new Promise&lt;any[]&gt;(resolve =&gt; {
            setTimeout(() =&gt; {
                resolve([{name:'Bleak'}, {name: 'Chris'}])
            }, 2000)
      }).then(users =&gt; {
            method(users)
      })
    }
}


class Uesr {
    @RequestDecorator('https://www.baidu.com')
    public all(users: any[]) {
      console.log(users)
    }
}
</code></pre>
<p>我们可以通过方法装饰器工厂来实现异步网络请求,这样的好处是我们以后想要请求得时候就会变得非常简单,我们使用装饰器就自动请求了,自动注入到我们的参数里面,我们直接用就可以了。</p>
<h2 id="属性修饰器和参数修饰器">属性修饰器和参数修饰器</h2>
<pre><code class="language-tsx">const PropDecorator: PropertyDecorator = (target: Object, propertyKey: string | symbol): void =&gt; {
    console.log(target)
    console.log(propertyKey)
}

class Hd {
    @PropDecorator
    public name: string | undefined
}

</code></pre>
<ul>
<li>属性装饰器的第一个参数target,如果我们是给静态属性添加的属性装饰器,那么target就是构造函数,如果是普通属性,那么target就是原型对象。</li>
<li>属性装饰器的第二个参数propertyKey是属性名称。</li>
</ul>
<pre><code class="language-tsx">const PropDecorator: PropertyDecorator = (target: Object, propertyKey: string | symbol): void =&gt; {
    // console.log(target)
    // console.log(propertyKey)
}

const ParamsDecorator: ParameterDecorator = (target: Object, propertyKey: string | symbol, parameterIndex: number): void =&gt; {
    console.log(target)
    console.log(propertyKey)
    console.log(parameterIndex)
}


class Hd {
    @PropDecorator
    public title: string | undefined

    public show(id: number = 1, compouted: boolean ,@ParamsDecorator content:string) {

    }
}
</code></pre>
<ul>
<li>参数装饰器的第一个参数target就是原型对象。</li>
<li>参数装饰器的第二个参数propertyKey是参数名称。</li>
<li>参数装饰器的第三个参数parameterIndex是参数所在的位置。</li>
</ul>
<h2 id="属性访问器动态转换对象属性">属性访问器动态转换对象属性</h2>
<pre><code class="language-tsx">// Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
// Object.defineProperty(obj, prop, descriptor)
// obj 要定义属性的对象。
// prop 要定义或修改的属性的名称或 Symbol。
// descriptor 要定义或修改的属性描述符。
const LowerDecorator: PropertyDecorator = (target: Object, propertyKey: string | symbol): void =&gt; {
    let value: string
    Object.defineProperty(target, propertyKey, {
      get: () =&gt; {
            return value.toLowerCase()
      },
      set: v =&gt; {
            value = v
      }
    })
}


class Hd {
    @LowerDecorator
    public title : string | undefined

}

const obj = new Hd()
obj.title = 'Bleak'
console.log(obj.title) // bleak
</code></pre>
<h2 id="使用ts的属性装饰器创建随机色块">使用ts的属性装饰器创建随机色块</h2>
<pre><code class="language-tsx">const RandomColorDecorator: PropertyDecorator = (target: Object, propertyKey: string | symbol): void =&gt; {
    const color:string[] = '0123456789abcdef'.split('')
    Object.defineProperty(target, propertyKey, {
      get: ()=&gt; {
            let res = '#'
            for(let i = 0; i &lt; 6; i++) {
                res += color
            }
            return res
      }
    })
}

class Hd {
    @RandomColorDecorator
    public color: string | undefined

    public draw() {
      document.body.insertAdjacentHTML('beforeend'
            ,`&lt;div style="height:200px;width:200px;background-color:${this.color};"&gt;Bleak&lt;/div&gt;`
      )
    }

}

console.log(new Hd().draw())
</code></pre>
<p><img src="https://img2022.cnblogs.com/blog/2550942/202204/2550942-20220411120330774-1753855160.gif"></p>
<h2 id="元数据reflect-metadata的使用">元数据<code>reflect-metadata</code>的使用</h2>
<p>元数据(metadata):即数据的数据,可以在数据中保存信息,我们可以根据元数据来在类上、类原型的属性添加元数据。</p>
<ol>
<li>首先我们需要安装这个库:<code>pm install reflect-metadata --save</code></li>
<li>然后我们使用这个库中的<code>Reflect.getMetadata</code>和<code>Reflect.defineMetadata</code>以及<code>Reflect.metadata</code></li>
</ol>
<pre><code class="language-tsx">import 'reflect-metadata'


// metadata元数据:数据的数据
let dreamCode = {
    name: 'bleak'
}

Reflect.defineMetadata('bleak',{url:'https://www.cnblogs.com/bleaka/'}, dreamCode, 'name')

console.log(Reflect.getMetadata('bleak', dreamCode, 'name')) // { url: 'https://www.cnblogs.com/bleaka/' }
</code></pre>
<p>关于三个重要的api<code>Reflect.getMetadata</code>和<code>Reflect.defineMetadata</code>以及<code>Reflect.metadata</code>的参数解释:</p>
<h3 id="reflectgetmetadata"><code>Reflect.getMetadata</code></h3>
<p>其可以接收两个参数或者是三个参数:</p>
<ul>
<li>当接收两个参数的时候,是去找'类'所映射的对应关系使用,第一个参数是创建映射时候的'key',第二个 参数是这个'类'。</li>
<li>当接收三个参数的时候,是去找'类中属性'对应的映射关系时候,三个参数,第一个参数是创建映射时候的'key' ,第二个参数是这个'实例',第三个是实例中所对应的具体'属性',其实可以很容易理解这里 为什么用的是实例,因为有了实例才有了属性。</li>
</ul>
<h3 id="reflectdefinemetadata"><code>Reflect.defineMetadata</code></h3>
<p>其可以给'类'和'属性'增加自定义映射关系。</p>
<ul>
<li>当只有这三个参数'metadataKey,metadataValue, target'修饰类时,第一个参数代表映射的'key',第二个参数代表映射'key'对应的'value',第三个参数代表的需要映射对应的类。</li>
<li>当有四个参数''metadataKey,metadataValue, target, propertyKey'修饰属性时,第一个参数代表映射的'key',第二个参数代表映射'key'对应的'value',第三个参数代表的需要映射对应的类或实例,第四个参数代表实例上的属性。</li>
</ul>
<h3 id="reflectmetadata"><code>Reflect.metadata</code></h3>
<pre><code class="language-tsx">import 'reflect-metadata'

@Reflect.metadata('name', 'A')
class A {
@Reflect.metadata('hello', 'world')
public hello(): string {
    return 'hello world'
}
}

Reflect.getMetadata('name', A) // 'A'
Reflect.getMetadata('hello', new A()) // 'world'

</code></pre>
<p>其可以给'类'和'属性'增加自定义映射关系,一般是传入两个参数,第一个参数代表映射的'key',第二个参数代表映射'key'对应的'value'。</p>
<h2 id="使用reflect-metadata的definemetadata和getmetadata配置验证数据">使用Reflect-metadata的defineMetadata和getMetadata配置验证数据</h2>
<pre><code class="language-tsx">import 'reflect-metadata'


const RequiredDecorator:ParameterDecorator = (target: Object, propertyKey: string | symbol, parameterIndex: number): void =&gt; {
    let requiredParams: number[] = Reflect.getMetadata('validate',target,propertyKey) || []
    requiredParams.push(parameterIndex)
    Reflect.defineMetadata('validate',requiredParams, target, propertyKey)
}

const validateDecorator:MethodDecorator = (target: Object, propertyKey: string | symbol, descriptor:PropertyDescriptor): PropertyDescriptor | void =&gt; {
    const method = descriptor.value
    descriptor.value = function() {
      let requiredParams:number[] = Reflect.getMetadata('validate',target,propertyKey) || []
      requiredParams.forEach(index =&gt; {
            if(index &gt; arguments.length || arguments === undefined) {
                throw new Error('请传递必要的参数')
            }
      })
      return method.apply(this, arguments)
    }
   
}

class User {
    @validateDecorator
    find(@RequiredDecorator name:string, @RequiredDecorator id: number) {
      console.log(id)
    }
}

new User().find('sds', 2)
</code></pre>
<p>以上,如果传递参数少于2个的话就会报错,提示请传递必要的参数。</p><br><br>
来源:https://www.cnblogs.com/bleaka/p/16123393.html
頁: [1]
查看完整版本: TypeScript装饰器Decorators学习