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

fuse-ts

v0.2.2

Published

Dependency injection for TypeScript

Downloads

3

Readme

fuse-ts

Build Status

fuse-ts is a dependency injection library for TypeScript. It allows you to use a @fused decorator on your TypeScript classes, so that when an instance of that class is constructed, it will receive instances of the types the class depends on.

Basic example

import {fuse, fused} from 'fuse-ts';

class Service {
  public someMethod(): void { }
}

class ServiceImplementation implements Service {
  public someMethod(): void { }
}

@fused
class ServiceConsumer {
  constructor(public service?: Service) { }
}

// let fuse know that all Service dependencies are
// to be instances of ServiceImplementation
fuse(Service).to(ServiceImplementation);

var serviceConsumer: ServiceConsumer = new ServiceConsumer();

// this will be true
var isServiceImplementation: boolean = serviceConsumer.service instanceof ServiceImplementation;

As you can see in the example, the ServiceConsumer constructor has one optional argument of the type Service. Upon creating an instance by using new ServiceConsumer(), fuse-ts will for each constructor argument look for a type that has been "fused" to the type of that argument, inject an instance in the constructor. So, in the example, a ServiceImplementation instance is injected in the ServiceConsumer constructor. This is made possible by the @fused decorator on the ServiceConsumer class.

Important

To use fuse-ts in your project, make sure you use the --experimentalDecorators and --emitDecoratorMetadata flags when running tsc, or use the following in your tsconfig.json file:

{
  ...
  "experimentalDecorators": true,
  "emitDecoratorMetadata": true
  ...
}

In depth

Decorated classes

When you decorate a TypeScript class with @fused, the class's constructor is wrapped by a function that resolves values for the original constructor's arguments, based on their type. For convenience, it is preferable to make all the constructor's arguments you want injected optional, so you can call the constructor without TypeScript compiler complaints. Like this:

@fused
class Component {
  constructor(a?: A, b?: B) {
    // stuff
  }
}

var component = new Component();

The following won't compile:

@fused
class Component {
  constructor(a: A, b: B) {
    // stuff
  }
}

// Fails with "Supplied parameters do not match any signature of call target."
var component = new Component();

If there are arguments you always want to pass to the constructor (ie. never injected by fuse-ts), make them required:

@fused
class Component {
  constructor(value: number, a?: A, b?: B) {
    // stuff
  }
}

var component = new Component(1);

Injected types

To make a type injectable, it must be registered with the library. This is done by calling fuse(BaseType).to(InjectedType). See the following example:

class BaseType {
  public someMethod(): void { }
}

class InjectedType implements BaseType {
  public someMethod(): void {
    // stuff
  }
}

// register with fuse-ts
fuse(BaseType).to(InjectedType);

You probably noticed that InjectedType implements a class instead of a TypeScript interface. Since interfaces are a design-time feature of TypeScript, they are not available in the transpiled JavaScript at run time. Because of that and the way fuse-ts works (for now), interfaces are unsuitable as a type to resolve to. So the following won't work:

interface Foo {
  fooIt(): void;
}

class InjectedFoo implements Foo {
  public fooIt(): void {
    // stuff
  }
}

// register with fuse-ts
fuse(Foo).to(InjectedFoo); // Compilation fails with "Cannot find name 'Foo'"

Object lifetime

Transient dependencies

When you use fuse(A).to(B), each injected instance of A will resolve to a new instance of B. This is called a transient dependency. This is the default behaviour of fuse-ts. To make this explicit in your code, you can use the asTransient() method on the result of fuse(A).to(B).

An example:

class Service {
  public serviceMethod(): void {}
}

class SingletonServiceImplementation implements Service {
  public serviceMethod(): void {
    // stuff
  }
}

fuse(Service).to(SingletonServiceImplementation).asTransient();

@fused
class ServiceConsumer {
  constructor(public service?: Service) { }
}

var firstInstance = new ServiceConsumer();
var secondInstance = new ServiceConsumer();

// The following  will be true
firstInstance.service !== secondInstance.service;

Singletons

Sometimes you want a service to be one and the same instance across your whole project, in other words, you want it to be a singleton. By default, each time fuse-ts injects a dependency, an new instance of that dependency is created. To make fuse-ts inject a singleton, you use the following:

class Service {
  public serviceMethod(): void {}
}

class SingletonServiceImplementation implements Service {
  public serviceMethod(): void {
    // stuff
  }
}

fuse(Service).to(SingletonServiceImplementation).asSingleton();

Now, each time a class that depends on Service is instantiated, it will receive the same SingletonServiceImplementation instance:

@fused
class ServiceConsumer {
  constructor(public service?: Service) { }
}

var firstInstance = new ServiceConsumer();
var secondInstance = new ServiceConsumer();

// The following  will be true
firstInstance.service === secondInstance.service;

Why?

This was created as part of a first personal venture into TypeScript, and inspired by the .NET Ninject DI library. Comments and complaints are welcome.