What is a mixed with

Simply put, you add attributes and values to an object (class).

For example, add the Scale attribute to the Base class (object).

type Constructor = new (... arg: Function Scale<TBase extends Constructor>(Base:) function Scale<TBase extends Constructor>(Base: TBase){ return class Scaling extends Base{ _scale = 0 get Scale(): number{ return this._scale } setScale(scale: Number){this._scale = scalse}}} // use class Sprite {name = ""; x = 0; y = 0; constructor(name:string){ this.name = name } } const EightBitSprite = Scale(Sprite); const ins = new EightBitSprite("Bird"); Ins. SetScale (0.8)Copy the code

Constrained Mixins

In the mixin process, you may need to call base-class specific methods. In the above case, we can’t get the underlying information of the base class. Then, for development convenience, you need a type of base class with a specific attribute.

How it works: TS/JS constructors can return an object and use this feature to “add” specific properties that we need to access. And since types don’t invade JS, this is fine.

Details are as follows:

// T defaults to an empty object. // The constructor can return an object to limit mixing of objects. // Except for enumerated types, TS does not intrude into the JS runtime, so the types are removed completely. Type GConstructor<T = {}> = new (... Constructor<{setPosition: (x:number, y:number) => void}>; // Classes can also be used as types, so you can use the generic type Sprite = GConstructor<Sprite> type Loggable = GConstructor<{print: () => void}> // Pick the first to mix into function Jumpable<TBase extends Positionable>(Base:) TBase){ return class Jumpable extends Base{ Jump(x:number, Y :number){this.setPosition(x, y)}}}Copy the code

Alternative patterns

The above cases are mixed singly. What about mixing in multiple objects?

The following example blends objects directly into the prototype at runtime to ensure that the type system is consistent with the runtime.

class Jumpable{

class Duckable{

class Sprite{
    x = 0;
    y = 0;

interface Sprite extends Jumpable, Duckable {}
applyMixin(Sprite, [Jumpable, Duckable])

function applyMixin(derivedCtor: any, constructors: any[]){
    constructors.forEach( baseCtor =>{
        Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
            Object.getOwnPropertyDescriptor(baseCtor.prototype, name) ||
Copy the code


You cannot use decorators to complete blending

const Pausable = (target: typeof Player) => {
    return calss Pausable extends target{
        shouldFreeze = false

class Player{
    x = 0;
    y = 0

const player = new Player()

// carsh
// The Player class does not have the decorator's type merged:
Copy the code


type FreezablePlayer = Player & { shouldFreeze: boolean };

const playerTwo = (new Player() as unknown) as FreezablePlayer;

Copy the code

Static attribute mixing

The generic T of Gen in the following form has a different scope than the generic T of the base() function and can cause an error.

The details are as follows:

function base<T>() { return class Base { static prop: T; }} // Base class expressions cannot reference class type parameters. Class Gen<T> extends base<T>() {} Class Spec extends Gen<string> {}Copy the code

A way around the above mechanism:

Use functions to keep the scope of generics consistent

function base<T>() {  
    return class Base {    
        static prop: T;  

function derived<T>() {  
    return class Derived extends base<T>() {    
        static anotherProp: T;  

class Spec extends derived<string>() {}
class AnotherSpec extends derived<number>() {}

Spec.prop; // string

Spec.anotherProp; // string
AnotherSpec.prop; // number
Spec.anotherProp; // number
Copy the code