Skip to content

Instantly share code, notes, and snippets.

@hediet
Last active June 15, 2020 06:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hediet/955d98bd4aea56aff860971c5a429764 to your computer and use it in GitHub Desktop.
Save hediet/955d98bd4aea56aff860971c5a429764 to your computer and use it in GitHub Desktop.
Avoid String Ids In Dependency Injection

Avoid String Ids In Dependency Injection

Option 1: String Ids

const myService = "MyService";

interface MyService {
    doSth(): void;
}

class MyConsumer {
    constructor(@inject(/* (1) */ myService) myService: /* (2) */ MyService) {
    }
}

At (1), you can specify an arbitrary string. User of such a framework are not forced to use string constants, they could just use plain strings. If they do that, it will be very hard to rename those strings as they have no meaning besides being text.

At (2), you can specify an arbitrary type.

This makes it very hard to discover what you can import.

Option 2: ServiceId<T>

// A ServiceId combines an id and a type. The id is for display purposes only.
// A global counter could be used to make it actually unique.
const myService = new ServiceId<MyService>("MyService");

interface MyService {
    doSth(): void;
}

class MyConsumer {
    constructor(@inject(/* (1) */ myService) myService: /* (2) */ typeof myService.T) {
    }
}

At (1), @inject expects a ServiceRef<T> instance. Users of this framework need to declare some const. No user would accidentally use new ServiceId<MyService>("myService") here.

At (2) you can still specify an arbitrary type - TypeScripts type system cannot enforce anything here. However, an ESLint rule could check that for every @inject($x) $y: $t, $t must be typeof $x.T. This would effectively yield static type safety.

You could use this implementation of ServiceRef<T>:

class ServiceRef<T> {
    public get T() {
       throw new Error("This property must not be used in non-type positions.");
    }
    
    constructor(public readonly id: string) {
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment