microsoft/tsyringe

Abstract classes as injection token

Opened this issue ยท 4 comments

Abstract classes could be a neat alternative to the awkward interface issue, i.e. no need for a magic string or an additional symbol export if you can use the abstract class value as the token.

e.g. convert this:

export interface MyInterface {
  value: string
}

to this:

export abstract class MyInterface {
  abstract value: string
}

See example below.

Currently this doesn't actually work because "Cannot assign an abstract constructor type to a non-abstract constructor type".

Maybe the InjectionToken type could be extended to permit abstract constructors?

// SuperService.ts
export abstract class SuperService {
  abstract value: string;
  // ...
}

// TestService.ts
import {SuperService} from "./SuperService";
// it's ok to implement (not extend) a class ๐Ÿ‘ 
export class TestService implements SuperService {
  constructor(public value: string) {}
  //...
}
// Client.ts
import {injectable, inject} from "tsyringe";
import { SuperService } from "./SuperService";
@injectable()
export class Client {
  // no more @inject("SuperService") 
  constructor(private service: SuperService) {}
}
// main.ts
import "reflect-metadata";
import {Client} from "./Client";
import {TestService} from "./TestService";
import {container} from "tsyringe";

container.register(SuperService, {
  useClass: TestService
});

const client = container.resolve(Client);
// client's dependencies will have been resolved

Maybe Token could just be anything except null | undefined?


Perhaps related to #20.

Probably the same topic as #108 ?

@tarohi24 You're right, this does seem like a duplicate.

I'll let a team member close this in hopes it will refresh their memory first.

ISTR the problem last time I tried to implement this one could do container.register(MyAbstractClass, { useClass: MyAbstractClass }); then when you resolve the token, it actually worked (which it shouldn't). TS has come a long way since I tried that though, so there may be a way to prevent an abstract class to be put in a ClassProvider. I may have some time to take a look at this in the next few weeks.

@MeltingMosaic Thanks for working on this issue. How about putting the type new (...args: any[]) => any; into ClassProvider ? This type only accepts constructors of non-abstract classes.