前言: 本类文章初衷只用作记录个人的学习博客,哪里有漏补哪里,不做任何其他商业用途。欢迎讨论,不喜勿喷。后面要是有遗漏的相关知识点,也会相应的补上。如果本篇文章能帮到你,我也会很愉快,共勉😁
目录
- 编译环境
- 装饰器
- 踩坑
编译环境
全局安装Typescript:
- npm install typescript -g
全局安装ts的编译工具
- npm install ts-node -g
ts-node xxx.ts运行ts文件
装饰器
装饰器是一种特殊的声明,它能够被附加到类声明、方法、访问符、属性和参数上。装饰器使用@fun这种形式,fun为一个函数,它会在运行时被调用,被装饰的声明信息做为参数传入。
若要使用装饰器,需先在tsconfig.json中启用experimentalDecorators选项:
{
"compilerOptions": {
"target": "ES5",
"experimentalDecorators": true
}
}
装饰器的写法
普通装饰器
function Format(data) {
console.log(data) // [Function: Greeter]
}
@Format
class Greeter {}
const greeter = new Greeter()
装饰器工厂
装饰器工厂和普通装饰器的写法区别:
- 普通装饰器无法传参,装饰器工厂可传参
- 普通装饰器的默认参数是一个被装饰的声明信息,装饰器工厂的参数是自己传入的参数
- 装饰器工厂需要返回一个函数,该函数会接受一个被装饰的声明信息
function Format(value: string) {
return (target) => {
console.log(value) // valid
console.log(target) // [Function: Greeter]
}
}
@Format('valid')
class Greeter {}
const greeter = new Greeter()
装饰器组合
当多个装饰器应用于一个声明上时,会进行如下步骤的操作:
- 由上至下一次对装饰器表达式求值
- 求值的结果会被当做函数,由下至上依次调用
function Format(value: string) {
return (target) => {
console.log('Format')
}
}
function ReflectTarget(target) {
console.log('ReflectTarget')
}
@ReflectTarget
@Format('valid')
class Greeter {}
const greeter = new Greeter()
/**
* 执行顺序:
* Format
* ReflectTarget
*/
类装饰器
在类装饰器中,普通装饰器的参数,以及装饰器工厂返回函数的参数都是类的constructor。
function ReflectTarget(target) {
console.log('ReflectTarget', target)
target.prototype.status = 'success'
target.prototype.sayHi = () => 'Hello World';
}
@ReflectTarget
class Greeter {
[x: string]: any;
constructor() {}
}
const greeter = new Greeter()
console.log(greeter.status) // success
console.log(greeter.sayHi()) // Hello World
方法装饰器
方法装饰器声明在一个方法的声明之前。可以用来监视,修改或者替换方法定义。
方法装饰器会在运行时当做函数被调用,传入以下3个参数:
- 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
- 成员的key
- 成员的属性描述符
下面是一个修改最后返回结果的一个方法装饰器:
function RefVoid() {
console.log('ReflectTarget')
return (target: any, key: any, descriptor: any) => {
descriptor.value = (value: number) => {
return value * 2
}
}
}
class Greeter {
constructor() {}
@RefVoid()
log(value: number) {
return value
}
}
const greeter = new Greeter()
console.log(greeter.log(10)) // 20
访问器装饰器
声明在一个访问器的声明前。用以监视,修改或替换一个访问器的定义。参数与方法装饰器一致。
function RefConfigurable() {
console.log('RefConfigurable')
return (target: any, key: any, descriptor: any) => {
console.log(target, key, descriptor)
descriptor.configurable = false
}
}
class Greeter {
x: number = 10
constructor() {
}
@RefConfigurable()
get getX() {
return this.x
}
}
const greeter = new Greeter()
console.log(greeter.getX) // 10
属性装饰器
声明在一个属性声明之前。会在运行时当做函数被调用,传入下列2个参数:
- 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
- 成员的key
function RefAttribute()() {
console.log('RefAttribute')
return (target: any, key: any) => {
console.log(target, key)
}
}
class Greeter {
@RefAttribute()
count: number = 10
constructor() {
}
}
const greeter = new Greeter()
console.log(greeter.count) // 10
参数装饰器
声明在一个方法参数的声明之前。会在运行时当做函数被调用,参数装饰器的返回值会被忽略,且只能用来监视一个方法的参数是否被传入 传入下列3个参数:
- 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
- 方法名字
- 参数在方法中的索引
function RefRequired() {
console.log('RefRequired')
return (target: any, key: any, index: number) => {
console.log(index) // 1
}
}
class Greeter {
constructor() {
}
log(msg: string, @RefRequired() count: number) {
return msg
}
}
const greeter = new Greeter()
console.log(greeter.log('Hello', 12)) // Hello
踩坑
- 在
nextJs使用装饰器会报错,这是next在提示缺少装饰器的babel,需要在.babelrc中配置兼容写法。
报错如下:
// x Unexpected token `@`. Expected identifier, string literal, numeric literal or [ for the computed key
解决方法:
{
"presets": ["next/babel"],
"plugins": [
"@emotion",
// 以下两行兼容装饰器写法
"babel-plugin-transform-typescript-metadata",
["@babel/plugin-proposal-decorators", { "legacy": true }]
]
}