TypeScript中的Decorator元数据反射:从⼩⽩到专家(部分
IV)
TypeScript 中的 Decorator & 元数据反射:从⼩⽩到专家(部分 IV)
龙逸楠 · 2016年04⽉13⽇
本⽂译⾃:
深⼊探寻 TypeScript 的装饰器实现,发现它们是如何为 JavaScript 添加令⼈兴奋的特性,⽐如反射和依赖注⼊。
这个系列包含4篇⽂章:
部分 II:属性注解与类装饰器
部分 III:参数装饰器与装饰器⼯⼚
部分 IV:类型的序列化与元数据反射 API
我会假设你已经读过了这个系列的前⼏篇⽂章。
在前⾯的⽂章中我们已经知道了什么是装饰器和 TypeScript 是怎么实现装饰器的。我们知道了如何在类、⽅法、属性和参数上使⽤装饰器,如何创建⼀个装饰器⼯⼚,如何使⽤⼀个装饰器⼯⼚,如何实现⼀个可配置的装饰器⼯⼚。
在本篇⽂章中,我们将会了解到:
1. 我们为什么需要 JavaScript 中的反射
2. 元数据反射 API
3. 基本类型序列
4. 复杂类型序列
让我们从学习为什么需要 Javascript 中的反射开始。
1. 我们为什么需要 JavaScript 中的反射
反射这个词⽤来描述那些可以检查同⼀个系统中其它代码(或⾃⼰)的代码。
反射在⼀些⽤例下⾮常有⽤(组合/依赖注⼊,运⾏时类型检查,测试)。
我们的 Javascript 应⽤变得越来越⼤,我们开始需要⼀些⼯具(⽐如控制反转容器)和功能(运⾏时类型检测)来管理不断增长的复杂度。问题在于如果 Javascript 没有反射,⼀些⼯具和功能就⽆法实现,或者⾄少它们不能实现得像它们在 C# 或者 Java 中的那么强⼤。
⼀个强⼤的反射 API 可以让我们在运⾏时检测⼀个未知的对象并且得到它的所有信息。我们要能通过反射得到以下的信息:
这个实例的名字
这个实例的类型
这个实例实现了哪个接⼝
这个实例的属性的名字和类型
这个实例构造函数的参数名和类型
在 JavaScript 中我们可以通过 或 函数获取⼀些实例的信息,但是我们还需要反射来实现更加强⼤的开发⼯具。
然⽽事情有所转机,因为 TypeScript 已经开始⽀持⼀些反射的功能。让我们看⼀下这些功能:
2. 元数据反射 API
原⽣ Javascript 对元数据反射的⽀持处于早期的开发阶段。这⾥是线上的。
Typescript 团队的⼀些⼈已经开始实现 ,Typescript 的编译器已经可以。
我们可以引⼊ 库来使⽤元数据反射 API:
npm install reflect-metadata
我们必须随 TypeScript 1.5+ ⼀起使⽤这个库并且将编译参数 emitDecoratorMetadata 设为 true。我们也必须包含对 reflect-metadata.d.ts 的引⽤并加载 Reflect.js ⽂件。
随后我们可以实现我们⾃⼰的装饰器并且使⽤⼀个可⽤的元数据设计键。到⽬前为⽌,只有三个可⽤的键:
类型元数据使⽤元数据键"design:type"
参数类型元数据使⽤元数据键"design:paramtypes"
返回值类型元数据使⽤元数据键"design:returntype"
让我们来看⼀组例⼦:
A) 使⽤元数据反射 API 获取类型元数据
让我们声明下⾯的属性装饰器 :
function logType(target : any, key : string) {
var t = Metadata("design:type", target, key);
console.log(`${key} type: ${t.name}`);
}
然后我们可以将它应⽤到类的⼀个属性上来获取它的类型 :
class Demo{
@logType // apply property decorator
public attr1 : string;
}
上⾯例⼦在控制台的输出 :
attr1 type: String
B) 使⽤元数据反射 API 获取参数类型元数据
让我们声明如下的参数装饰器 :
function logParamTypes(target : any, key : string) {
var types = Metadata("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 // apply parameter decorator
doSomething(
param1 : string,
param2 : number,
param3 : Foo,小白学java有前途吗
param4 : { test : string },
param5 : IFoo,
param6 : Function,
param7 : (a : number) => void,
) : number {
return 1
}
}
上⾯例⼦在控制台的输出 :
doSomething param types: String, Number, Foo, Object, Object, Function, Function
C) 使⽤元数据反射 API 获取返回类型元数据
我们也可以使⽤ "design:returntype" 元数据键来获取⼀个⽅法上的返回类型信息:
3. 基本类型序列化
让我们再来看⼀次上⾯的 design:paramtypes 例⼦。我们注意到接⼝ IFoo 和字⾯量对象 { test: string} 都序列化为 Object。这是因为TypeScript 只⽀持基础类型的序列化。基础类型的序列化规则是:
number 序列化为 Number
string 序列化为 String
boolean 序列化为 Boolean
any 序列化为 Object
void 序列化为 undefined
Array 序列化为 Array
如果是⼀个多元组,序列化为 Array
如果是⼀个类,序列化为 class constructor
如果是⼀个枚举,序列化为 Number
如果⾄少有⼀个调⽤签名,序列化为 Function
其它的序列化为 Object (包括接⼝)
接⼝和字⾯量对象在未来可能会被序列化为复杂类型序列,但是这个特性现在还不能⽤。
4. 复杂类型序列
TypeScript 团队正致⼒于⼀个能让我们⽣成复杂类型元数据的提案。
这个提案描述了⼀些复杂的类型如何被序列化。上⾯的那些序列化规则依然会被⽤于基本类型序列化,但是复杂的类型序列化使⽤的是不同的序列化逻辑。这是提案中的⼀个基本类型⽤来描述所有可能的类型:
/**
* Basic shape for a type.
*/
interface _Type {
/**
* Describes the specific shape of the type.
* @remarks
* One of: "typeparameter", "typereference", "interface", "tuple", "union",
* or "function".
*/
kind: string;
}
我们也可以到⼀些⽤来描述所有可能类型的类。⽐如,我们可以到序列化范性接⼝ interface foo<bar> { /* ... */} 的类:
/**
* Describes a generic interface.
*/
interface InterfaceType extends _Type {
kind: string; // "interface"
/**
* Generic type parameters for the type. May be undefined.
*/
typeParameters?: TypeParameter[];
/**
* Implemented interfaces.
*/
implements?: Type[];
/**
* Members for the type. May be undefined.
* @remarks Contains property, accessor, and method declarations.
*/
members?: { [key: string | symbol | number]: Type; };
/**
* Call signatures for the type. May be undefined.
*/
call?: Signature[];
/**
* Construct signatures for the type. May be undefined.
*/
construct?: Signature[];
/**
* Index signatures for the type. May be undefined.
*/
index?: Signature[];
}
如同我们在上⾯看到的,这⾥有⼀个属性指出实现了哪些接⼝:
/**
* Implemented interfaces.
*/
implements?: Type[];
这种信息可以⽤来在运⾏时验证⼀个实例是否实现了特定的接⼝,⽽这个功能对于⼀个 IoC 容器特别的有⽤。
我们不知道对复杂类型序列的⽀持什么时候会被加⼊到 TypeScript 的功能中,但我们已经迫不及待了因为我们计划⽤它为我们的JavaScript IoC 容器: 增加⼀些碉堡的特性。
5. 结论
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论