almin / ddd-base

DDD base class library for JavaScript application.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

mixin

azu opened this issue · comments

commented
export type Constructor<T> = new (...args: any[]) => T;
/*

    static mixin<T extends object, U extends object, M1 extends object, I1 extends object>(
        this: U & Constructor<T>,
        m1: (superClass: U & Constructor<T>) => MixinClass<M1, I1>
    ): M1 & U & Constructor<T & I1>;
    static mixin<T extends object,
        U extends object,
        M1 extends object,
        I1 extends object,
        M2 extends object,
        I2 extends object>(
        this: U & Constructor<T>, m1: (superClass: U & Constructor<T>) => MixinClass<M1, I1>, m2: (superClass: U & Constructor<T>) => MixinClass<M2, I2>): M1 & M2 & U & Constructor<T & I1 & I2>;
    static mixin<T extends object,
        U extends object,
        M1 extends object,
        I1 extends object,
        M2 extends object,
        I2 extends object,
        M3 extends object,
        I3 extends object>(
        this: U & Constructor<T>,
        m1: (superClass: U & Constructor<T>) => MixinClass<M1, I1>,
        m2: (superClass: U & Constructor<T>) => MixinClass<M2, I2>,
        m3: (superClass: U & Constructor<T>) => MixinClass<M3, I3>
    ): M1 & M2 & M3 & U & Constructor<T & I1 & I2 & I3>;
    static mixin(...mixins: ((superClass: any) => MixinClass<any, any>)[]) {
        // FIXME: workaround for https://github.com/Microsoft/TypeScript/issues/4130
        const mixinBuilder = new MixinBuilder(this);
        return (mixinBuilder as any).with(...mixins);
    }
 */
export function mixin<T extends object, U extends object, M1 extends object, I1 extends object>(
    superclass: U & Constructor<T>,
    m1: MixinClass<M1, I1>
): M1 & U & Constructor<T & I1>;
export function mixin<T extends object,
    U extends object,
    M1 extends object,
    I1 extends object,
    M2 extends object,
    I2 extends object>(
    superclass: U & Constructor<T>, m1: MixinClass<M1, I1>, m2: MixinClass<M2, I2>): M1 & M2 & U & Constructor<T & I1 & I2>;
export function mixin<T extends object,
    U extends object,
    M1 extends object,
    I1 extends object,
    M2 extends object,
    I2 extends object,
    M3 extends object,
    I3 extends object>(
    superclass: U & Constructor<T>,
    m1: MixinClass<M1, I1>,
    m2: MixinClass<M2, I2>,
    m3: MixinClass<M3, I3>
): M1 & M2 & M3 & U & Constructor<T & I1 & I2 & I3>;
export function mixin<T extends object, U extends object>(
    superclass: U & Constructor<T>,
    ...mixins: MixinClass<any, any>[]
) {
    return mixins.reduce((c, mixin) => mixin(c), superclass) as Constructor<T>
}

export type Properties<T> = { [K in keyof T]: T[K] };
export type MixinClass<T, P> = Properties<T> & Constructor<P>;

export class MixinBuilder<T extends object, U extends object> {
    private superclass: Constructor<T> & U;

    constructor(superclass: Constructor<T> & U) {
        this.superclass = superclass;
    }

    with<M1 extends object, I1 extends object>(): Constructor<T & I1>;
    with<M1 extends object, I1 extends object>(m1: MixinClass<M1, I1>): M1 & U & Constructor<T & I1>;
    with<M1 extends object, I1 extends object, M2 extends object, I2 extends object>(
        m1: MixinClass<M1, I1>,
        m2: MixinClass<M2, I2>
    ): M1 & M2 & U & Constructor<T & I1 & I2>;
    with<M1 extends object,
        I1 extends object,
        M2 extends object,
        I2 extends object,
        M3 extends object,
        I3 extends object>(
        m1: MixinClass<M1, I1>,
        m2: MixinClass<M2, I2>,
        m3: MixinClass<M3, I3>
    ): M1 & M2 & M3 & U & Constructor<T & I1 & I2 & I3>;
    with(...mixins: Function[]): Constructor<T> {
        return mixins.reduce((c, mixin) => mixin(c), this.superclass) as Constructor<T>;
    }
}
commented

Copy

/**
 *
 * K is type of a key
 * T[K] is type of its value
 */
export type PartialMap<T> = { [K in keyof T]?: (prev: T[K]) => T[K] };

/**
 * Constructor type
 */
export type Constructor<T = {}> = new (...args: any[]) => T;

/**
 * Copyable can copy current instance and map new value
 */
export interface CopyableMethod<T> {
    copy(partial: Partial<T>): T;

    mapCopy(partial: PartialMap<T>): T;
}

export type Properties<T> = { [K in keyof T]: T[K] };
/**
 * Mixin Copyable to the BaseClass
 * @param BaseClass
 * @returns Copyable class
 * @example
 *
 * class A extends Copyable<Entity<EntityIdentifier>>{}
 */
export const Copyable = <T extends Constructor>(BaseClass: T) => {
    return class extends BaseClass {
        /**
         * Return partial change of this object
         *
         * e.g)
         * `new Person("jack", 2).copy({age: 10})` is  `new Person("jack", 10)`
         *
         */
        copy(partial: Partial<Properties<this>>): this {
            const Prototype = Object.getPrototypeOf(this);
            const newInstance = Object.create(Prototype);
            return Object.assign(newInstance, partial);
        }

        /**
         * Return partial change of this object by using functions
         *
         * e.g)
         * `new Person("jack", 10).mapCopy({age: prev => prev+1})` is  `new Person("jack", 11)`
         *
         * @param {PartialMap<T>} partial
         * @returns {T}
         */
        mapCopy(partial: PartialMap<this>): this {
            const Prototype = Object.getPrototypeOf(this);
            const newInstance = Object.create(Prototype);
            const oldInstance: { [index: string]: any } = this;
            for (const key of Object.keys(this)) {
                if (key in partial) {
                    newInstance[key] = (partial as any)[key](oldInstance[key]);
                } else {
                    newInstance[key] = oldInstance[key];
                }
            }
            return newInstance;
        }
    };
};
commented

Note: mixin should support ValueObject

class AbstractBookmark extends ValueObject<BookmarkProps>{}
export class Bookmark extends Copyable(AbstractBookmark) {
}