日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

ts可展开注释_TS语法之装饰器(注解)

發布時間:2024/4/14 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ts可展开注释_TS语法之装饰器(注解) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

寫在前面的話

本文只講解 TypeScript 中的裝飾器語法(下稱注解), 只會告訴你如何編寫一個自定義注解,且通過注解簡單的修改邏輯,不涉及 反射 或 元編程 等其他更進一步的代碼講解,如果有興趣可以自行搜索相關的進階寫法,比如這位:

開始

注1:裝飾器僅用于 class 語法中,所以以下舉例中只會使用 class 寫法。(其實是我只測試了 class 語法中的使用,而且也沒必要在普通方法上使用注解)

注2:tsconfig.json 中 target 必須為 ES5 或以上版本。且 experimentalDecorators 與 emitDecoratorMetadata 字段需為 true

由于我們寫的是 TypeScript,我們首先定義幾個 interface 和 type ,讓 IDE 有一些基本提示:

type Prototype = {

constructor: Function

} & any

?

type Constructor = { new(...args: any[]): {} };

?

interface FunctionAnnotation {

(target: Prototype, propertyKey: PropertyKey, descriptor: TypedPropertyDescriptor): void;

}

?

interface ConstructorAnnotation {

(constructor: T): T;

}

?

interface PropertyAnnotation {

(target: Prototype, propertyKey: PropertyKey): void;

}

?

interface ParameterAnnotation {

(target: Prototype, propertyKey: PropertyKey, parameterIndex: number): void;

}

?

其中,PropertyKey 和 TypedPropertyDescriptor 都在 lib.es5.d.ts 文件中有定義,其值為:

declare type PropertyKey = string | number | symbol;

interface TypedPropertyDescriptor {

enumerable?: boolean;

configurable?: boolean;

writable?: boolean;

value?: T;

get?: () => T;

set?: (value: T) => void;

}

如果有人不喜歡泛型寫法,也可以將 TypedPropertyDescriptor 替換成 PropertyDescriptor

interface PropertyDescriptor {

configurable?: boolean;

enumerable?: boolean;

value?: any;

writable?: boolean;

get?(): any;

set?(v: any): void;

}這里使用泛型僅僅是因為我不喜歡 any,Prototype 類型中加上 any 是因為沒有辦法只能用 any,原因是類型定義中沒有辦法用 symbol 類型的值定義成 key,否則就可以寫成 type Prototype = {[key: string]: Function; [key: number]: Function; [key: symbol]: Function;} 了

如果有人不知道 PropertyDescriptor 是干嘛的,請參考 Object.defineProperty()

下面我們直接用例子教你如何自定義一個注解。

方法注解

寫法:

export function logFuncCall(): FunctionAnnotation {

return function (target, propertyKey, descriptor) {

if (typeof descriptor.value === "function") {

let value: Function = descriptor.value;

// @ts-ignore

descriptor.value = function (...args: any) {

console.log(`${Date()} ${target.constructor.name}["${String(propertyKey)}"] be Called`);

return value.call(this, ...args);

};

}

};

}

注:若注解中需要傳入參數,參考正常方法的寫法添加需要的參數即可,不贅述。(注解與正常方法的區別僅僅在于方法調用前面多了一個@符號而已)

參數詳解:target:被注解類的 prototype ,本例中為 Test.prototype,如果被注解的方法是靜態方法,則為類本身,也就是 Test

propertyKey:被注解的方法名,本例中為 method

descriptor:屬性描述,參考 Object.defineProperty(o, p, attributes)中 attributes 參數

需要注意的是,如果注解中不需要傳入任何參數,則可以省略注解中最外層的一層方法,變成如下:

export function logFuncCall(target, propertyKey, descriptor) {

if (typeof descriptor.value === "function") {

let value: Function = descriptor.value;

// @ts-ignore

descriptor.value = function (...args: any) {

console.log(`${target.constructor.name}["${String(propertyKey)}"] be Called`);

return value.call(this, ...args);

};

}

}

這時,注解則可以寫成 @logFuncCall 的形式(去掉了括號),否則括號不能省略。下同,不再贅述。本文并未采用省略寫法。

直接編譯調試運行如下代碼:

class Test {

@logFuncCall()

method() {}

}

let t = new Test("ttt");

t.method();

這時,控制臺中會輸出:

Test["method"] be Called

Getter/Setter注解

同 方法注解;

與方法注解的區別在于,Getter/Setter 注解的 descriptor 中只有對應的 get 和 set 字段,而 value 字段為 undefined,而方法注解只中有 value 字段,而 get 和 set 字段為 undefined

所以通用的方法注解應該如下:

export function logFuncCall(): FunctionAnnotation {

return function (target, propertyKey, descriptor) {

if (typeof descriptor.value === "function") {

let value: Function = descriptor.value;

// @ts-ignore

descriptor.value = function (...args: any) {

console.log(`${target.constructor.name}["${String(propertyKey)}"] be Called`);

return value.call(this, ...args);

};

}

if (typeof descriptor.get === "function") {

let get = descriptor.get;

descriptor.get = function () {

console.log(`${target.constructor.name}["get ${String(propertyKey)}"] be Called`);

return get.call(this);

};

}

if (typeof descriptor.set === "function") {

let set = descriptor.set;

descriptor.set = function (value) {

console.log(`${target.constructor.name}["set ${String(propertyKey)}"] be Called`);

set.call(this, value);

};

}

};

}

類注解

寫法:

export function logCreate(): ConstructorAnnotation {

return (constructor: T) => {

return class extends constructor {

constructor(...args: any[]) {

console.log(`${Date()} ${constructor.name} new an instance`);

super(...args);

}

};

};

}

參數詳解constructor:被注解類的原對象,而不是類的 prototype

上面這種寫法適用于大部分情況,類似于繼承了被注解的類

如果你不希望任何人重寫此類的方法,可以用如下的匿名寫法:(在類的 constructor 方法中返回一個對象,則 new 對象時 this 指向該對象,而非類的實例)

export function logCreate(): ConstructorAnnotation {

return (constructor: T) => {

return class {

constructor(...args: any[]) {

console.log(`${Date()} ${constructor.name} new an instance`);

return new constructor(...args);

}

};

};

}

這種寫法需要注意的是:環境中被注解的類將會變為一個注解返回的匿名內部類,其不繼承于原先的類,在類外也無法調用原先類的所有方法,只有使用 new 新建對象后,才會返回一個原先類的對象(除非你在內部類中自己定義了靜態方法去調用原先類的靜態方法)

被這種寫法注解的類如果被繼承,則只能在構造函數中對自身做出一定修改,且所有靜態方法不可通過類名調用

屬性注解

寫法:

export function logGetSet(): PropertyAnnotation {

return function (target, propertyKey) {

let s = "_" + String(propertyKey);

Object.defineProperty(target, propertyKey, {

get() {

console.log(`get ${String(propertyKey)}`);

return this[s];

},

set(v) {

console.log(`set ${String(propertyKey)} => ${v}`);

this[s] = v;

},

configurable: true,

enumerable: true,

});

};

}

參數詳解target:參考方法注解

propertyKey:參考方法注解

注:propertyKey 是有可能為 symbol 或 number 類型的,所以 String() 大概率不能省略

這里需要注意的是:由于注解執行緊跟在類的定義之后,所以如果沒有在定義時賦默認值,則 target 中是不會有對應的屬性字段的,這時可以選擇自己定義 Getter/Setter 然后在其中編寫對應的方法,也可以選擇在項目中引入 import 'reflect-metadata',使用 反射-元數據 編程(參考本文開頭給出的鏈接 )

如果注解的是靜態字段,且在定義時就賦過值,則 target 中將會有對應的屬性字段且有值

參數注解

寫法:

export function logParam(): ParameterAnnotation {

return function (target, propertyKey, parameterIndex) {

...

};

}

參數詳解target:參考方法注解

propertyKey:參考方法注解

parameterIndex:被注解參數位置,從 0 開始

這里除了反射以外好像沒別的用法?或者跟方法注解聯動,畢竟方法也是對象,是可以保存參數的,案例:

export function logFuncCall(): FunctionAnnotation {

return function (target, propertyKey, descriptor) {

if (typeof descriptor.value === "function") {

let value: Function = descriptor.value;

// @ts-ignore

descriptor.value = function (...args: any) {

// @ts-ignore

value.logParam?.forEach(i => console.log(`第${i}參數: ${args[i]}`));

return value.call(this, ...args);

};

}

}

export function logParam(): ParameterAnnotation {

return function (target, propertyKey, parameterIndex) {

target[propertyKey].logParam = [...target[propertyKey].logParam ?? [], parameterIndex];

};

}

class Test {

@logFuncCall()

set(@logParam() log: string) {}

}

new Test.set("aaa")

> 第0參數: aaa

這樣同時注解 @logFuncCall() 和 @logParam 之后,就可以打印傳入的參數了

結尾

另外附上一些自己寫的小案例:

type Prototype = {

constructor: Function

__proto__: Prototype

} & any

?

/** (運行時)當父類中沒有對應方法時提示報錯 */

export function override(): FunctionAnnotation {

return (target, propertyKey) => {

if (target.__proto__?.[propertyKey] === undefined) {

console.error(`Method "${String(propertyKey)}" Not A Override Function`);

}

};

}

?

/** 自動調用父類的對應方法 */

export function autoCallSuper(): FunctionAnnotation {

return (target, propertyKey, descriptor) => {

if (typeof descriptor.value === "function") {

let value = descriptor.value;

if (typeof target.__proto__?.[propertyKey] === "function") {

// @ts-ignore

descriptor.value = function (...args: any) {

target.__proto__[propertyKey].apply(this, args);

return value.apply(this, args);

};

} else {

console.error(`${target.__proto__.constructor.name} No Function Name is "${String(propertyKey)}"`);

}

} else {

console.warn(`"autoCallSuper" Annotation Only Used On NormalFunction, Not Getter/Setter`);

}

};

}

測試案例:

@logCreate()

class A {

@logFuncCall()

a() {

console.log("A");

}

}

?

@logCreate()

class B extends A {

constructor() {

super();

this.a();

}

@logFuncCall()

@autoCallSuper()

a() {

console.log("B");

}

static b() {}

}

?

console.dir(B);

console.dir(new B());

B.b();

調試 log:

[class (anonymous) extends B] // console.dir(B);

B new an instance // new B()

A new an instance // super被調用

B["a"] be Called // 調用a方法

A["a"] be Called // 由于autoCallSuper,先調用super方法

A // super方法打印A

B // 重寫方法打印B

B {} // console.dir(new B());打印new出來的對象

Function["b"] be Called // B.b();

如果類注釋采用了匿名寫法,則 log 為:

[class (anonymous)] // 匿名類沒有繼承B

B new an instance // new B()

A new an instance // super被調用,但是返回了A對象,導致this指針變為了A對象的實例

A["a"] be Called // this.a()

A // 方法打印A

A {} // console.dir(new B());打印new出來的對象

Uncaught TypeError: B.b is not a function // 匿名類沒有繼承B,自然也就沒有B里的靜態方法,報錯

最后,小心溢出,不用

最后,性能太低,不用

最后,還沒成真的語法呢,不用

最后,編寫一時爽,debug火葬場

總結

以上是生活随笔為你收集整理的ts可展开注释_TS语法之装饰器(注解)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。