Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mixin #5

Open
azu opened this issue May 6, 2018 · 2 comments
Open

mixin #5

azu opened this issue May 6, 2018 · 2 comments

Comments

@azu
Copy link
Member

azu commented May 6, 2018

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>;
    }
}
@azu
Copy link
Member Author

azu commented May 6, 2018

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;
        }
    };
};

@azu
Copy link
Member Author

azu commented May 20, 2018

Note: mixin should support ValueObject

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant