Gathering detailed insights and metrics for mixwith.ts
Gathering detailed insights and metrics for mixwith.ts
Gathering detailed insights and metrics for mixwith.ts
Gathering detailed insights and metrics for mixwith.ts
TypeScript compatible version of Justin Fagani's mixin library for ES6
npm install mixwith.ts
Module System
Min. Node Version
Typescript Support
Node Version
NPM Version
11 Stars
39 Commits
1 Watching
1 Branches
1 Contributors
Updated on 06 Aug 2024
Minified
Minified + Gzipped
TypeScript (100%)
Cumulative downloads
Total Downloads
Last day
0%
12
Compared to previous day
Last week
-40%
39
Compared to previous week
Last month
155.2%
171
Compared to previous month
Last year
3,670%
1,508
Compared to previous year
3
A TypeScript fork of Justin Fagnani's excellent mixwith.js library that supports TypeScript type completions at design time.
mixwith
differs from other mixin approaches because it does not copy properties from one object to another. Instead, mixwith
works with "subclass factories" which create a new class that extends a superclass with the mixin.
my-mixin.ts:
1const MyMixin = (s: Constructable) => class extends s { 2 // mixin methods here 3}
my-class.ts
1class MyClass extends mix(MySuperClass).with(MyMixin) { 2 // class methods here, go ahead, use super! 3}
Using npm
npm i mixwith.ts
Using deno
import {mix, Constructable } from "https://deno.land/x/mixwithts/mod.ts"
Classes use mixins in their extends
clause. Classes that use mixins can define and override constructors and methods as usual. In conflicts, the right most Mixin wins.
1 2class MySuperClass { 3 // ... 4} 5 6const MyMixin = <c extends Constructable>(s : c) => class extends s { 7 foo() {} 8} 9 10class MyClass extends mix(MySuperClass).with(MyMixin) { 11 foo() { 12 super.foo() // calls MyMixin.foo() 13 } 14}
To use contructors and make sure the base class constructors are called, mixins consume and pass along constructor arguments using a constructor with ...args: any
. Mixins can optionally consume constructor arguments as well:
1class MySuperClass { 2 constructor(numArg : number, strArg: string) { 3 } 4 } 5 6 const MyMixin = <c extends Constructable>(s: c) => 7 class Mix1 extends s { 8 constructor(...args: any[]) { 9 super(...args); 10 } 11 12 }; 13 14 class MixedClass extends mix(MySuperClass).with(MyMixin) { 15 constructor(numArg : number, strArg: string) { 16 super(numArg, strArg); 17 } 18 } 19 20 21 const myInstance : MixedClass = new MixedClass(42, "hello world"); 22
In this example, I model a 2D war game that has common functionality across units (shooters, spawners, etc.).
1// deno-lint-ignore-file no-unused-vars 2import type { Constructable } from "./mod.ts"; 3import {mix } from "./mod.ts"; 4interface Position { 5 xPos: number; 6 yPos: number; 7} 8 9class AirForce implements Position { 10 xPos = 0; 11 yPos = 0; 12} 13 14class GroundForce implements Position { 15 xPos = 0; 16 yPos = 0; 17} 18 19const bomber = <c extends Constructable>(s : c) => class extends s { 20 bomb() { } 21} 22 23const shooter = <c extends Constructable>(s : c)=> class extends s { 24 shoot() { } 25} 26 27//Notice that this takes a 'Position' type since 28//it needs to refer back to its super class (which implements Position) 29const spawner = <c extends Constructable<Position>>(s : c) => class extends s { 30 spawn() { 31 //spawn logic for things that spawn 32 this.yPos = Math.random() * 100; 33 this.xPos = Math.random() * 100; 34 } 35} 36 37//airplanes spawn, shoot, and bomb 38class Airplane extends mix(AirForce).with(spawner, shooter, bomber) { } 39//helicopters spawn and shoot 40class Helicopter extends mix(AirForce).with(spawner, shooter) { } 41//tanks spawn and shoot 42class Tank extends mix(GroundForce).with(spawner, shooter) { } 43//fortifications don't spawn in, but do shoot 44class fortification extends mix(GroundForce).with(shooter) { } 45 46// You can also skip the final class definition, but intent is less clear 47const myJetFighter = new (mix(AirForce).with(spawner, shooter)) 48
The builder MixinBuilder
and its supporting method mix()
were modified:
MixinBuilder.with()
accepts up to six mixins rather than an array. This change was made to enable design time TypeScript completions. The source code can be modified to support more mixins as needed.MixinBuilder.with()
applies instanceOf support for mixins automaticallyTo resolve Mixins at design time, two types were defined.
1export type Constructable<T = {}> = new (...args: any[]) => T; 2export type mixin<C extends Constructable, T> = (args: C) => T
Constructable<T>
defines a constructor function (can be called with new) that creates instances of objects of type T
.mixin
defines a function that takes a Constructable<T>
and returns a new type T
.TypeScript will infer both T
and C
implicitly. However, if you want to refer to a super's properties from a mixin without red squigglies, reference it.
1//open ended mixin. Use to add new functionality to any type 2const openMix = (s: Constructable) => class extends s... 3 4//closed mixin. Use to apply to a specific interface or class 5const closedMix = (s: Constructable<MyInterface>) => class extends s... 6
The subclass factory pattern does not require a library. This library makes working with the pattern more powerful and easier to use.
instanceof
support to mixin functions. That is, the following test passes1class MyClass extends mix(MySuperClass).with(MyMixin) { 2 // class methods here, go ahead, use super! 3} 4 5const foo = new MyClass(); 6 7assert(foo instanceof MySuperClass); //true 8assert(foo instanceof MyMixin); //true
Subclass factory style mixins preserve the object-oriented inheritance properties that classes provide, like method overriding and super
calls, while letting you compose classes out of mixins without being constrained to a single inheritance hierarchy, and without monkey-patching or copying.
Methods in subclasses can naturally override methods in the mixin or superclass, and mixins override methods in the superclass. This means that precedence is preserved - the order is: subclass -> mixin__1 -> ... -> mixin__N -> superclass.
super
worksSubclasses and mixins can use super
normally, as defined in standard Javascript, and without needing the mixin library to do special chaining of functions.
Since super()
works, mixins can define constructors. Combined with ES6 rest arguments and the spread operator, mixins can have generic constructors that work with any super constructor by passing along all arguments.
Typical JavaScript mixins usually used to either mutate each instance as created, which can be bad for performance and maintainability, or modify a prototype, which means every object inheriting from that prototype gets the mixin. Subclass factories don't mutate objects, they define new classes to subclass, leaving the original superclass intact.
• MixinBuilder: Class MixinBuilder<Base>
MixinBuilder helper class (returned by mix()).
Name | Type |
---|---|
Base | extends Constructable |
Method: with()
1with<A, B, C, D, E, F>( 2 a, 3 b?, 4 c?, 5 d?, 6 e?, 7 f?): Base & A & B & C & D & E & F
Applies a chain of mixins to a base class. The method supports up to six mixins. The mixins are applied in reverse sequence (e.g. the right most mixin is applied first, etc.)
Parameter |
---|
A extends Constructable |
B extends Constructable |
C extends Constructable |
D extends Constructable |
E extends Constructable |
F extends Constructable |
Parameter | Type | Description |
---|---|---|
a | mixin < Base , A > | A mixin to apply. |
b ? | mixin < A , B > | A mixin to apply (optional). |
c ? | mixin < B , C > | A mixin to apply (optional). |
d ? | mixin < C , D > | A mixin to apply (optional). |
e ? | mixin < D , E > | A mixin to apply (optional). |
f ? | mixin < E , F > | A mixin to apply (optional). |
Base
& A
& B
& C
& D
& E
& F
▸ BareMixin<C
, T
>(mixin
): mixin<C, T>
A basic mixin decorator that applies the mixin using the apply function so that it can be used with isApplicationOf, hasMixin, and other mixin decorator functions.
Name | Type | Description |
---|---|---|
C | extends Constructable | The constructor type representing the original superclass. |
T | T | The return type of the mixin function. |
Name | Type | Description |
---|---|---|
mixin | mixin<C, T> | The mixin to wrap. |
mixin<C, T>
▸ Cached<C
, T
>(mixin
): mixin<C, any>
Decorates mixin
so that it caches its applications. When applied multiple
times to the same superclass, mixin
will only create one subclass, memoize
it and return it for each application.
Note: If mixin
somehow stores properties its classes constructor (static
properties), or on its classes prototype, it will be shared across all
applications of mixin
to a super class. It's reccomended that mixin
only
access instance state.
Name | Type | Description |
---|---|---|
C | extends Constructable | The type of the constructor of the mixin. |
T | T | The type of the mixin instance. |
Name | Type | Description |
---|---|---|
mixin | mixin<C, T> | The mixin to be cached. |
mixin<C, any>
▸ DeDupe<C
, T
>(mixin
): mixin<C, T>
Decorates mixin
so that it only applies if it's not already on the
prototype chain.
Name | Type | Description |
---|---|---|
C | extends Constructable | The constructor type representing the original superclass. |
T | T | The return type of the mixin function. |
Name | Type | Description |
---|---|---|
mixin | mixin<C, T> | The mixin function to be deduplicated. |
mixin<C, T>
▸ HasInstance<T
>(mixin
): T
Adds a Symbol.hasInstance implementation to the provided mixin object to enable the use of the
instanceof
operator with instances of classes that include the mixin.
Name | Description |
---|---|
T | The type of the mixin object. |
Name | Type | Description |
---|---|---|
mixin | T | The mixin object to be enhanced with the Symbol.hasInstance implementation. |
T
▸ Mixin<C
, T
>(mixin
): mixin<C, T>
Decorates a mixin function to add deduplication, application caching, and instanceof support.
Name | Type | Description |
---|---|---|
C | extends Constructable | The constructor type representing the original superclass. |
T | T | The return type of the mixin function. |
Name | Type | Description |
---|---|---|
mixin | mixin<C, T> | The mixin to wrap. |
mixin<C, T>
▸ apply<C
, T
>(superclass
, mixin
): T
Applies mixin
to superclass
.
apply
stores a reference from the mixin application to the unwrapped mixin
to make isApplicationOf
and hasMixin
work.
This function is useful for mixin wrappers that want to automatically enable hasMixin support.
Name | Type | Description |
---|---|---|
C | extends Constructable | The constructor type of the superclass. |
T | T | The resulting type of the mixin. |
Name | Type | Description |
---|---|---|
superclass | C | The superclass to which the mixin will be applied. |
mixin | mixin<C, T> | The mixin function that provides additional behavior to the superclass. |
T
▸ hasMixin<T
>(o
, mixin
): boolean
Checks if the provided mixin has been applied to the given prototype object.
Name | Description |
---|---|
T | The type of the mixin. |
Name | Type | Description |
---|---|---|
o | object | - |
mixin | T | A mixin function used with apply. |
boolean
▸ isApplicationOf<T
>(proto
, mixin
): boolean
Returns true
iff proto
is a prototype created by the application of
mixin
to a superclass.
isApplicationOf
works by checking that proto
has a reference to mixin
as created by apply
.
Name | Description |
---|---|
T | The type of the mixin. |
Name | Type | Description |
---|---|---|
proto | object | A prototype object created by apply. |
mixin | T | A mixin function used with apply. |
boolean
whether proto
is a prototype created by the application of
mixin
to a superclass
▸ mix<C
>(superclass?
): MixinBuilder<C>
A fluent interface to apply a list of mixins to a superclass.
1class X extends mix(Object).with(A, B, C) {}
The mixins are applied in order to the superclass, so the prototype chain will be: X->C'->B'->A'->Object.
This is purely a convenience function. The above example is equivalent to:
1C = Mixin(C) 2B = Mixin(B) 3A = Mixin(A) 4class X extends C(B(A(Object))) {}
Function
Name | Type |
---|---|
C | extends Constructable |
Name | Type | Description |
---|---|---|
superclass? | C | The superclass to which the mixin will be applied. If not defined, it defaults to class {} . |
MixinBuilder<C>
▸ unwrap<T
>(wrapper
): T
Unwraps the function wrapper
to return the original function wrapped by
one or more calls to wrap
. Returns wrapper
if it's not a wrapped
function.
Name | Description |
---|---|
T | The type of the wrapped mixin. |
Name | Type | Description |
---|---|---|
wrapper | T | The wrapped mixin. |
T
▸ wrap<C
, T
>(mixin
, wrapper
): mixin<C, T>
Sets up the function mixin
to be wrapped by the function wrapper
, while
allowing properties on mixin
to be available via wrapper
, and allowing
wrapper
to be unwrapped to get to the original function.
wrap
does two things:
mixin
to wrapper
so that properties set on
mixin
inherited by wrapper
.mixin
that points back to mixin
so that
it can be retreived from wrapper
Function
Name | Type | Description |
---|---|---|
C | extends Constructable | The type of the constructor of the mixin. |
T | T | The type of the mixin instance. |
Name | Type | Description |
---|---|---|
mixin | mixin<C, T> | The mixin to be wrapped. |
wrapper | mixin<C, T> | The wrapper mixin. |
mixin<C, T>
No vulnerabilities found.
No security vulnerabilities found.