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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@umapplication.lab.i.t.m2d.in/traits-ts

v1.0.4

Published

Install using yarn

Readme

Traits(Mixin) for Typescript

Already merged into UMApplication

How to install

Install using yarn

yarn add @umapplication.lab.i.t.m2d.in/traits-ts

or using npm

npm install @umapplication.lab.i.t.m2d.in/traits-ts

and import in your project

How to use

UMA-Traits is extremely easy to use.

Here is an example case, and all following tutorials are basing on this case:


class Human {

  constructor(
    public Name: string
  ) { }

  public Speak() {
    console.log('Human can speak');
  }
}

class Designer {
  public Draw() {
    console.log('Designer can draw');
  }
}

class Developer {
  public Coding() {
    console.log('Developer can coding');
  }
}

Here we got 2 roles: Designer & Developer and a base class: Human, it is impossible to have multiple of them in one single person without some Magic.

Now, with UMA-Traits, we change simply do that:

class Clarkson extends (Traits.Basing(Human).With(Designer, Developer)) {
}

const clarksonInstance = new Clarkson();

// Now Clarkson can get these things done
clarksonInstance.Speak();
clarksonInstance.Draw();
clarksonInstance.Coding();

Limitation to traits

You should noticed that only Basing class can have a Constructor with Arguments.

The actual execution order of constructors in above case is like:

function Clarkson(...args: any[]) {

  Human(...args);
  Designer();
  Developer();

}

Determine if a class extended a trait

Keyword "instanceof" is still worked for base class but not traits.

Here is the real situation you are facing:


(clarksonInstance) instanceof Human; // true
(clarksonInstance) instanceof Designer; // false
(clarksonInstance) instanceof Developer; // false

Using helper methods can easily do this better and even better than "instanceof".

Traits.Having(Clarkson, Human); // true
Traits.Having(clarksonInstance, Human); // true

Traits.Having(Clarkson, Designer); // true
Traits.Having(clarksonInstance, Designer); // true

Traits.Having(Clarkson, Developer); // true
Traits.Having(clarksonInstance, Developer); // true

And also worked with class which using Clarkson as base class

class LittleClarkson extends Clarkson {
}

Traits.Having(LittleClarkson, Human); // true
Traits.Having(LittleClarkson, Designer); // true
Traits.Having(LittleClarkson, Developer); // true

Difference to UMApplication.Traits

Traits in UMA is just a wrapper of this project, no different other than that.

About issues

Since we are no longer use github as our major repo platform, processing of issues on github may be procrastinated, please pay a patience and we are sorry about that.

Additional: Why Traits(Mixin)

Trait is a good replacement of "Extending Multiple Classes".

In fact, Many modern OOP languages already implemented this feature as a "native" part or providing a guideline for developers who need this.

Typescript also provide a example shows how to do this in typescript.

Let we talk about those languages who are not implemented natively.

There are two ways to simulate trait/mixin like "real" native implementations.

1. Create trait using both class/structure and interface, create its instance while creating parent class instance

This way used by Golang and Java.

Usually, developer need to create class/structure for actual implementation and interface for extending.

Here is a sample code snippets in java:

interface Plant {
    String getColor();

    void setColor(String val);
}

interface Food {
    String getTaste();

    void setTaste(String val);
}

class FoodImp implements Food {
    private String taste;

    @Override
    public String getTaste() {
        return taste;
    }

    @Override
    public void setTaste(String val) {
        taste = val;
    }

}

class PlantImpl implements Plant {
    private String color;

    @Override
    public String getColor() {
        return color;
    }

    @Override
    public void setColor(String val) {
        color = val;
    }
}

class Pair<T, U> {
    private T left;
    private U right;

    public Pair(T left, U right) {
        this.left = left;
        this.right = right;
    }

    public T getLeft() {
        return left;
    }
    public U getRight() {
        return right;
    }

    public static <T, U> Pair<T, U> of(T left, U right) {
        return new Pair<T, U>(left, right);
    }
}

class MixinProxy implements InvocationHandler {

    private final Map<String, Object> delegates;
    public MixinProxy(Pair<?, Class<?>> ...pairs) {
        delegates = new HashMap<>();
        for (Pair<?, Class<?>> pair: pairs) {
            for (Method method: pair.getRight().getMethods()) {
                String methodName = method.getName();
                if (!delegates.containsKey(methodName)) {
                    delegates.put(methodName, pair.getLeft());
                }
            }
        }
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object delegate = delegates.get(method.getName());
        return method.invoke(delegate, args);
    }

    public static Object newInstance(Pair<?, Class<?>> ...pairs) {
        Set<Class<?>> interfaces = Arrays.stream(pairs).map(pair -> pair.getRight()).collect(Collectors.toSet());
        return Proxy.newProxyInstance(pairs[0].getLeft().getClass().getClassLoader(), interfaces.toArray(new Class<?>[pairs.length]), new MixinProxy(pairs));
    }
}

public class Test {
    public static void main(String[] args) {
        Object vegetable = MixinProxy.newInstance(
            Pair.of(new FoodImp(), Food.class),
            Pair.of(new PlantImpl(), Plant.class)
        );

        Food food = (Food) vegetable;
        Plant plant = (Plant) vegetable;

        food.setTaste("good");
        plant.setColor("green");

        System.out.println("Taste is " + food.getTaste());
        System.out.println("Color is " + plant.getColor());
    }

The problem of above is "Properties" cannot be shared between Traits.

2. Merge trait symbols into target class

This way is only work on script languages or may need some 'Magic'.

Not very common in non-script languages.

Here is a sample in Typescript

// Each mixin is a traditional ES class
class Jumpable {
  jump() {}
}
 
class Duckable {
  duck() {}
}
 
// Including the base
class Sprite {
  x = 0;
  y = 0;
}
 
// Then you create an interface which merges
// the expected mixins with the same name as your base
interface Sprite extends Jumpable, Duckable {}
// Apply the mixins into the base class via
// the JS at runtime
applyMixins(Sprite, [Jumpable, Duckable]);
 
let player = new Sprite();
player.jump();
console.log(player.x, player.y);
 
// This can live anywhere in your codebase:
function applyMixins(derivedCtor: any, constructors: any[]) {
  constructors.forEach((baseCtor) => {
    Object.getOwnPropertyNames(baseCtor.prototype).forEach((name) => {
      Object.defineProperty(
        derivedCtor.prototype,
        name,
        Object.getOwnPropertyDescriptor(baseCtor.prototype, name) ||
          Object.create(null)
      );
    });
  });
}

We strongly recommend not Defining interface and class using same name although they are pointing to one same "Class".

And also, this solution is not so Typescript.