@msom/reaction
Version:
@msom/reaction
97 lines (95 loc) • 2.89 kB
text/typescript
import {
ComputedDecoratorUsedError,
defineProperty,
initComponentDefinition,
isComponent,
} from "@msom/common";
import { _computed, generateIObserver } from "../utils";
import { ComputedOption } from "../Computed";
/**
* 计算属性装饰器
* 用于get计算属性和无参函数上
* @returns MethodDecorator
*/
export function computed<T>(
option?: Omit<ComputedOption<T>, "method">
): (
target: object,
key: string | symbol,
descriptor: TypedPropertyDescriptor<T>
) => TypedPropertyDescriptor<T> | void {
return function (target, key, descriptor) {
// 实例属性target为类的原型对象
// 静态属性target为类构造器
// 非静态
if (typeof target === "function") {
throw new ComputedDecoratorUsedError({ NotStatic: true });
}
if (!descriptor) {
throw new ComputedDecoratorUsedError({ NotProperty: true });
}
let method = descriptor.value as (() => T) | undefined;
let methodType: "value" | "get" = "value";
if (typeof method !== "function") {
method = descriptor.get;
methodType = "get";
}
if (typeof method !== "function") {
throw new ComputedDecoratorUsedError({
defineMessage: () => {
return `the method or accessor for getter ${String(
key
)} not be function.`;
},
});
}
if (method.length > 0) {
console.warn(
new ComputedDecoratorUsedError({
defineMessage: () => {
return "the computed effct method should be no argument";
},
}).message
);
return;
}
if (isComponent(target)) {
// 初始化组件定义
const definition = initComponentDefinition(target);
// 判断是否已经标记为响应式
if (
Reflect.has(definition.$observers, key) &&
definition.$observers[key]
) {
throw new ComputedDecoratorUsedError({
defineMessage: () => {
return `the computed method or computed property ${String(
key
)} is exist.`;
},
});
}
// 标记
defineProperty(definition.$observers, key, 7, "computed");
}
const _method = method;
descriptor[methodType] = function (this: any) {
const create = generateIObserver.bind(this);
return create(key, _computed, {
...option,
method: _method.bind(this),
}).get();
} as T & (() => T);
const setter = descriptor.set;
if (setter) {
descriptor.set = function (this: any, value: T) {
setter.call(this, value);
const create = generateIObserver.bind(this);
create(key, _computed, {
...option,
method: _method.bind(this),
}).notify();
};
}
};
}