npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

ts-mix

v0.7.1

Published

Yet another library for the implementation of mixins in TypeScript...

Downloads

13

Readme

Yet another library for implementing mixins in TypeScript.

Dependencies

  • Typescript
  • ES7 Decorator
  • "experimentalDecorators": true, in tsconfig.json

Installation

npm i --save ts-mix

Features

ts-mix is a simple, lightweight library for implementing a mixins pattern in a TypeScript

Usage example

Create mixin with interface

interface IMixinA {
    propInMixin: number;
    readonly testA: string;
    readonly initInSetup: Date;
    methodInMixin(): string;
    test(): void;
    sameNameMethod(): string;
}

const mixinAName = 'mixinA' as const;
//                                              \  props init in method setup  /
const mixinA = mixin<typeof mixinAName, IMixinA, ['initInSetup', 'propInMixin']>({
    mixinName: mixinAName,

    setup() {
        return {
            propInMixin: 123111,
            initInSetup: new Date(),
        };
    },

    init() {
        console.log(this.target.propInMixin); // 123111
        console.log(this.target.mixins.mixinA.propInMixin); // 123111
        this.propInMixin = 123;
        console.log(this.target.propInMixin); // 123
        console.log(this.target.mixins.mixinA.propInMixin); // 123
    },

    get testA() {
        return 'test-a';
    },
    methodInMixin() {
        return this.testA;
    },
    test() {
        console.log(this.target.propInMixin); // 123
        console.log(this.target.mixins.mixinA.propInMixin); // 123
    },
    sameNameMethod(): string {
        return 'from mixinA';
    },
});
Create simple mixin
const mixinB = mixin({
    mixinName: 'mixinB' as const,
    sameNameMethod(): string {
        return 'from mixinB';
    },
    methodB() {
        return "test-b";
    },
});

const mixinC = mixin({
    mixinName: 'mixinC',
    methodC() {
        return 'methodC';
    },
});

Usage

Assigning mixins outside the class definition:
class Test {
    test = 'test';
}

const classC = useMixins(Test, mixinA, mixinB);

const instance = new classC();

console.log(haveMixin(instance, mixinA, classC)); // true
console.log(haveMixin(instance, mixinB, classC)); // true
console.log(haveMixin(instance, mixinC, classC)); // false

console.log(instance.test); // "test" - method class Test
console.log(instance.methodB()); // "test-b" - method from mixinB
console.log(instance.mixins.mixinB.methodB()); // "test-b" - method from mixinB
console.log(instance.methodInMixin()); // "test-a" - method from mixinA
Assigning mixins during class definition

Inheritance from result of methods useMixins(Class, ...mixins)

/* tslint:disable:max-classes-per-file variable-name */
export const TestUseMixinB = useMixins(class TestUseMixinB {
    name = 'b';
    fieldInB: string = 'test';

    constructor(readonly str: string) {
        //
    }
}, mixinA);

export const TestUseMixinBB = useMixins(class TestUseMixinBB extends TestUseMixinB {
    name = 'bb';
    fieldInBB: number = 1;

    constructor(str: string, readonly numb: number) {
        super(str);
    }
}, mixinB);

export const TestUseMixinBBB = useMixins(class TestUseMixinBBB extends TestUseMixinBB {
    name = 'bbb';
    asdasd = 1;
    fieldInBBB: boolean = true;

    constructor(readonly bool: boolean) {
        super('1', 1);
    }

    asdas(asdasd: boolean) {
        return 1;
    }
}, mixinC);
////// usage:

const testB = new TestUseMixinB('abc');
const testBB = new TestUseMixinBB('qwe', 987);
const testBBB = new TestUseMixinBBB(true);

console.log(testB.str); // 'abc'
console.log(haveMixin(testB, mixinA, TestUseMixinB)); // true

console.log(testBB.str); // 'qwe'
console.log(testBB.numb); // 987
console.log(haveMixin(testBB, mixinA, TestUseMixinB)); // true
console.log(haveMixin(testBB, mixinB, TestUseMixinBB)); // true

console.log(testBBB.str); // '1'
console.log(testBBB.numb); // 1
console.log(testBBB.bool); // true
console.log(haveMixin(testBBB, mixinA, TestUseMixinB)); // true
console.log(haveMixin(testBBB, mixinB, TestUseMixinBB)); // true
console.log(haveMixin(testBBB, mixinC, TestUseMixinBB)); // true

This method is the most convenient, so how:

  • works well with class inheritance.
  • no need for decorators or hacks with an additional interface
  • clear syntax

Assigning mixins during property decorator

Decorator @mixinsProp usage:

class TestDecorator {
    @mixinsProp(mixinB) mixins!: MixinsProp<[typeof mixinB]>;

    constructor() {
        console.log(Object.keys(this.mixins)); // 'mixinB'
    }
}
const test = new TestDecorator();

console.log(test.mixins.mixinB.methodB()); // 'test-b'
console.log(test.mixins.mixinB.sameNameMethod()); // 'from mixinB'

Use for Object:

Immutable:

const testBase = {
    test: 'abc',
    m() {
        return 'm';
    },
};

const instance = useMixinsForObject(testBase, mixinA, mixinB);
console.log(instance.test); // "abc" - method object testBase
console.log(instance.m()); // "m" - method object testBase
console.log(instance.methodB()); // "test-b" - method from mixinB
console.log(instance.mixins.mixinB.methodB()); // "test-b" - method from mixinB
console.log(instance.methodInMixin()); // "test-a" - method from mixinA

Mutable

const test = {
    test: 'abc',
    m() {
        return 'm';
    },
};

applyMixins(test, mixinA, mixinB);
console.log(test.test); // "abc" - method object testBase
console.log(test.m()); // "m" - method object testBase
if (haveMixins(test, [mixinA, mixinB])) {
    console.log(test.mixins.mixinB.methodB()); // "test-b" - method from mixinB
    console.log(test.mixins.mixinB.methodB()); // "test-b" - method from mixinB
    console.log(test.mixins.mixinA.methodInMixin()); // "test-a" - method from mixinA
}

Mixins are used in the following order. Class extended only by methods and properties of mixins it does not have.

All methods of all mixins can be found in this.mixins. by mixin name.

console.log(instance.sameNameMethod()); // "from classA"
console.log(instance.mixins.mixinA.sameNameMethod()); // "from mixinA"
console.log(instance.mixins.mixinB.sameNameMethod()); // "from mixinB"

Rewrite some methods or fields in target

With property rewrite in mixin config, you can rewrite some fields in target object

When creating a mixin, you can specify to it specify target interface.

If the target does not satisfy the target interfaces of all the mixins, TypeScript will warn about it

const mixinD = mixin<
    'mixinD',
    { methodInD: () => string },
    [],
    { readonly fall: string, asd: () => string } // specify target interface
>(
    {
        mixinName: 'mixinD' as const,
        rewrite(th) {
            let fall = 'a';
            return {
                get fall() {
                    return 'fall in mixinD - ' + fall + ' - ' + th.origin.fall;
                },
                set fall(v: string) {
                    fall = v;
                },
                asd() {
                    return 'WWW';
                },
            };
        },
        methodInD() {
            return this.target.fall;
        },
    }
);

class Test {
    fall = 'test';
    asd() {
        return 'Hello my name ' + this.fall;
    }
    test() {
        return this.fall;
    }
}
const test = new Test();

console.log(test.fall); // 'test');
console.log(test.asd()); // 'Hello my name test');
console.log(test.test()); // 'test');

useMixinsForObject(test, mixinD); // if the target does not satisfy the target interfaces of all the mixins, TypeScript will warn about it

console.log(() => assertHaveMixin(test, mixinD, Test)).to.not.throw();
assertHaveMixin(test, mixinD, Test);

console.log('mixins' in test).true;
console.log(typeof test.mixins); // 'object');
console.log(test.fall); // 'fall in mixinD - a - test');
console.log(test.mixins.mixinD.methodInD()); // 'fall in mixinD - a - test');
console.log(test.mixins.mixinD.target.fall); // 'fall in mixinD - a - test');

test.fall = 'b';

console.log(test.fall); // 'fall in mixinD - b - test');
console.log(test.mixins.mixinD.methodInD()); // 'fall in mixinD - b - test');
console.log(test.mixins.mixinD.target.fall); // 'fall in mixinD - b - test');

console.log(test.test()); // 'fall in mixinD - b - test');

console.log(test.asd()); // 'WWW');

TypeGuards and Asserts methods

TypeGuards
  • haveMixin(obj, mixin, baseObject?) - check is object used some mixin
  • haveMixins(obj, [...mixins], baseObject?) - check is object used all mixins
  • isMixin<Mixin>(value) - check is value is any Mixin or concrete Mixin from generic (maybe is not correct)
Asserts
  • assertHaveMixin(obj, mixin, baseObject?) - checks if the object uses a mixin and if not, throws error
  • assertHaveMixins(obj, [...mixins], baseObject?) - checks if the object uses all mixins and if not, throws error

Example:

// connects mixins to the class
const ClassA = useMixins(class ClassA {
    // some methods and properties
}, mixinA, mixinB);


const instance = new ClassA();
console.log(haveMixin(instance, mixinA, ClassA)); // true
console.log(haveMixin(instance, mixinB, ClassA)); // true
console.log(haveMixin(instance, mixinC, ClassA)); // false

console.log(haveMixins(instance, [mixinA, mixinB], ClassA)); // true
console.log(haveMixins(instance, [mixinA, mixinB, mixinC], ClassA)); // false

assertHaveMixin(instance, mixinA, ClassA); // to errors
assertHaveMixin(instance, mixinB, ClassA); // to errors
assertHaveMixin(instance, mixinB, ClassA); // throw error MixinAssertError

assertHaveMixins(instance, [mixinA, mixinB], ClassA); // to errors
assertHaveMixins(instance, [mixinA, mixinB, mixinC], ClassA); // throw error MixinAssertError

Limitations

  • methods useMixins and other have limit 5 mixins

NPM Scripts

  • npm run build-dev - build development version
  • npm run build-prod - build production version
  • npm run pretest - compile sources before run unit tests
  • npm run test - start unit tests
  • npm run test-typing - start typings tests
  • npm run clean - build production version
  • npm run clean - build production version

Contributing

All contributions are welcome! To get started, simply fork and clone the repo, run npm install, and get to work. Once you have something you'd like to contribute, be sure to run npm test locally, then submit a PR.

Author

Viktor (VitProg) - [email protected]

My github - https://github.com/VitProg