本篇內容包括以下部分:npm
關於反射的概念,摘自百度百科json
在計算機科學領域,反射是指一類應用,它們可以自描述和自控制。也就是說,這類應用經過採用某種機制來實現對本身行爲的描述(self-representation)和監測(examination),並能根據自身行爲的狀態和結果,調整或修改應用所描述行爲的狀態和相關的語義。
可見反射機制對於依賴注入、運行時類型斷言、測試是很是有用的,同時隨着基於JavaScript的應用作的愈來愈大,使得咱們但願有一些工具和特性能夠用來應對增加的複雜度,例如控制反轉,運行時類型斷言等。但因爲JavaScript語言中沒有反射機制,因此致使這些東西要麼無法實現,要麼實現的不如C#
或Java
語言實現的強大。bash
強大的反射API容許咱們能夠在運行時測試一個未知的類,以及找到關於它的任何信息,包括:名稱、類型、接口等。雖然可使用諸如Object.getOwnPropertyDescriptor()
和Object.keys()
查詢到一些信息,但咱們須要反射來實現更強大的開發工具。慶幸的是,TypeScript已經支持反射機制,來看看這個特性吧函數
能夠經過安裝reflect-metadata
包來使用元數據反射的API工具
npm install reflect-metadata;
若要使用它,咱們須要在tsconfig.json
中設置emitDecoratorMetadata
爲true
,同時添加reflect-metadata.d.ts
的引用,同時加載Reflect.js
文件。而後咱們來實現裝飾器並使用反射元數據設計的鍵值,目前可用的有:開發工具
design:type
design:paramtypes
design:returntype
咱們來經過一組例子來講明測試
首先聲明以下的屬性裝飾器:設計
function logType(target : any, key : string) { var t = Reflect.getMetadata("design:type", target, key); console.log(`${key} type: ${t.name}`); }
接下來將其應用到一個類的屬性上,以獲取其類型:code
class Demo{ @logType public attr1 : string; }
這個例子將會在控制檯中打印以下信息:對象
attr1 type: String
聲明參數裝飾器以下:
function logParamTypes(target : any, key : string) { var types = Reflect.getMetadata("design:paramtypes", target, key); var s = types.map(a => a.name).join(); console.log(`${key} param types: ${s}`); }
而後將它應用在一個類方法的參數上,用以獲取所裝飾參數的類型:
class Foo {} interface IFoo {} class Demo{ @logParameters param1 : string, param2 : number, param3 : Foo, param4 : { test : string }, param5 : IFoo, param6 : Function, param7 : (a : number) => void, ) : number { return 1 } }
這個例子的執行結果是:
doSomething param types: String, Number, Foo, Object, Object, Function, Function
一樣的咱們可使用"design:returntype"
元數據鍵值,來獲取一個方法的返回類型:
Reflect.getMetadata("design:returntype", target, key);
讓咱們回看上面關於"design:paramtypes"
的例子,注意到接口IFoo
和對象字面量{test: string}
被序列化爲Object
,這是由於TypeScript僅支持基本類型的序列化,基本類型序列化規則以下:
number
序列化爲Number
string
序列化爲String
boolean
序列化爲Boolean
any
序列化爲Object
void
序列化爲undefined
Array
序列化爲Array
Tuple
序列化爲Array
class
序列化爲類的構造函數Enum
序列化爲Number
Object
接口和對象字面量可能在以後的複雜類型序列化中會被作具體的處理。
TypeScript的團隊爲複雜類型的元數據序列化作出了努力。上面列出的序列化規則對基本類型依然適用,但對複雜類型提出了不一樣的序列化邏輯。以下是經過一個例子來描述全部可能的類型:
interface _Type { /** * Describes the specific shape of the type. * @remarks * One of: "typeparameter", "typereference", "interface", "tuple", "union" or "function". */ kind: string; }
咱們也能夠找到用於描述每種可能類型的類,例如用於序列化通用接口interface foo<bar>
:
// 描述一個通用接口 interface InterfaceType extends _Type { kind: string; // "interface" // 通用類型參數. 可能爲undefined. typeParameters?: TypeParameter[]; // 實現的接口. implements?: Type[]; // 類型的成員 可能爲undefined. members?: { [key: string | symbol | number]: Type; }; // 類型的調用標識. 可能爲undefined. call?: Signature[]; // 類型的構造標識. 可能爲undefined. construct?: Signature[]; // 類型的索引標識. 可能爲undefined. index?: Signature[]; }
這裏有一個屬性指出實現了哪些接口
// 實現的接口 implements?: Type[];
這種信息能夠用來在運行時驗證一個實例是否實現了特定的接口,而這個功能對於一個依賴翻轉容器特別的有用。