元数据
首先我们先来看一下元数据的用途有哪些,官网给的用途元数据可能会用于许多用途,例如调试、序列化或使用装饰器执行依赖项注入
。
看到这里元数据在开发中还是有些应用场景的(ps:在后端依赖注入和序列化还是很有场景的)。
案例分析
这里我们来分析一下官网的第一个案例。
ts
interface Context {
name: string;
metadata: Record<PropertyKey, unknown>;
}
function setMetadata(_target: any, context: Context) {
context.metadata[context.name] = true;
}
class SomeClass {
@setMetadata
foo = 123;
@setMetadata
accessor bar = "hello!";
@setMetadata
baz() { }
}
const ourMetadata = SomeClass[Symbol.metadata];
console.log(JSON.stringify(ourMetadata));
// { "bar": true, "baz": true, "foo": true }
首先我们先来分析一下metadata方法,其实我们看TypeScript最新的源代码中Symbol添加了一个新的属性在lib.esnext.decorators
文件中。
ts
/// <reference no-default-lib="true"/>
/// <reference lib="es2015.symbol" />
/// <reference lib="decorators" />
interface SymbolConstructor {
readonly metadata: unique symbol;
}
interface Function {
[Symbol.metadata]: DecoratorMetadata | null;
}
这个属性就是用来存储元数据的。在一个类中我们去调用[Symbol.metadata]
方法就可以获取到这个类中的元数据。我们可以在装饰器中去给这个元数据对象(通过源码查看DecoratorMetadata
的类型是Record<PropertyKey, unknown> & object
)注入数据,例如例子中的这段代码 context.metadata[context.name] = true;
。最后我们可以通过类调用[Symbol.metadata]
方法获取注入的内容。
应用场景
通过上面的案例我们已经了解元数据应该如何写了,让我们看一下开发中具体的案例来看一下元数据的场景。
ts
const serializables = new WeakMap<object, string[]>();
type Context =
| ClassAccessorDecoratorContext
| ClassGetterDecoratorContext
| ClassFieldDecoratorContext
;
export function serialize(_target: any, context: Context): void {
if (context.static || context.private) {
throw new Error("Can only serialize public instance members.")
}
if (typeof context.name !== "string") {
throw new Error("Can only serialize string properties.");
}
let propNames = serializables.get(context.metadata);
if (propNames === undefined) {
serializables.set(context.metadata, propNames = []);
}
propNames.push(context.name);
}
export function jsonify(instance: object): string {
const metadata = instance.constructor[Symbol.metadata];
const propNames = metadata && serializables.get(metadata);
if (!propNames) {
throw new Error("No members marked with @serialize.");
}
const pairStrings = propNames.map(key => {
const strKey = JSON.stringify(key);
const strValue = JSON.stringify((instance as any)[key]);
return `${strKey}: ${strValue}`;
});
return `{ ${pairStrings.join(", ")} }`;
}
class Person {
firstName: string;
lastName: string;
@serialize
age: number
@serialize
get fullName() {
return `${this.firstName} ${this.lastName}`;
}
toJSON() {
return jsonify(this)
}
constructor(firstName: string, lastName: string, age: number) {
}
}
这里是官方给的一个案例,具体的逻辑我就不解释了我想看一遍应该就能看懂,他实现的功能是通过jsonify去打印被serialize装饰器注入的字段。
总结
对于ts新支持的元数据特性我认为主要的应用场景可能是angular或者nodejs(服务端)的场景(ps:现在大趋势是函数式编程)。