Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

question: Using Token with Generics failed in stongly typying #992

Open
jeromeSH26 opened this issue Jan 11, 2023 · 0 comments
Open

question: Using Token with Generics failed in stongly typying #992

jeromeSH26 opened this issue Jan 11, 2023 · 0 comments
Labels
type: question Questions about the usage of the library.

Comments

@jeromeSH26
Copy link

jeromeSH26 commented Jan 11, 2023

Following some comments here on how to pass arguments to constructors on top of the injected properties, I'm using Token to do it as mentonned.
In my case, my class is extending another one, so I need to pass an argument to the child class to then bypass it tp the parent one (super(value)). For the example, I have added 2 args, but they could be more in the real program. That is why I also want to avoid using mixins, which could be a solution to avoid args in the class.

The solution I have with Token is working well.
However there is a "leak" of type safety when it comes to generics in Token.

Please check the example below. This is a simple DI Pattern for Repository. I have created a 'contract' with the Interface, and then created an implementation to this interface.
The implementation needs a DB_CONNECTION to simulate the connection to a real DB.
It needs also other properties, espacially one to be send to its parent class. They are defined in the generic type TClassParams
But I have to type the token related to this params as Token<any,any> which might create an issue when settings the values in the container. For example, the "value:U" field in the TClassParam type finaly is expecting a number, but I can pass a string or anything else.

Is there a way to do it better ? The initial requirement is to pass additionnal parameters in the constructor of the class that receive injection.
Thks

import "reflect-metadata";
import { Container, Inject, Service, Token } from "typedi";

//  for better DI management, I'm using interfaces as "contract"
interface IInterfaceRepo<TDataType extends Record<string, any>> {
  list(): Array<TDataType>;
  findOne<K extends keyof TDataType>(key: K, value: TDataType[K]): TDataType | undefined;
}

// type of the parameters of the first implementation of my interface
type TClassParams<T, U> = {
  initialData: Array<T>;
  value: U;
};

// data types manage by the repository 
type TData = { a: string; b: boolean };

// the parent class I want to extends. Doesn't need injected values
class CParent<T> {
  constructor(private value: T) {}
  showMeValeur() {
    return this.value;
  }
}

// creation of the tokens. One for the DB_CONNECTION and the other one for additional params
const DB_CONNECTION = new Token<string>("DB_CONNECTION");
const CLASS_PARAMS = new Token<TClassParams<any, any>>("CLASS_PARAMS");  //<--- note the ANY, ANY here. Type safty is lost

// the first implementation of the class 
@Service()
class RepoImpl<T extends Record<string, any>, U = number> extends CParent<U> implements IInterfaceRepo<T> {
  private _data: T[];
  constructor(
// injection of the DB_CONNECTION and additional params
    @Inject(DB_CONNECTION) private readonly _dbConnection: string,
    @Inject(CLASS_PARAMS) readonly _classParams: TClassParams<T, U>
  ) {
    super(_classParams.value); //<-- for example, on of the params is sent to the parent class
    this._data = _classParams.initialData;
  }
  list(): T[] {
    return this._data;
  }
  findOne<K extends keyof T>(key: K, value: T[K]): T | undefined {
    return this._data.find((d) => (d[key] = value));
  }

  showMeProperties() {
    console.log(this._data, "  --  ", this._dbConnection, " *** ", this.showMeValeur());
  }
}

const initialData: TData[] = [
  {
    a: "hi",
    b: false,
  },
];

Container.set(DB_CONNECTION, "connectio container");
Container.set(CLASS_PARAMS, {
  initialData: initialData,
  value: 4, <-- I can set a string or boolean here. As a result, program will crash at runtime
});

const repo = Container.get<RepoImpl<TData>>(RepoImpl<TData>);
console.log("list ", repo.list());
repo.showMeProperties();
@jeromeSH26 jeromeSH26 added the type: question Questions about the usage of the library. label Jan 11, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: question Questions about the usage of the library.
Development

No branches or pull requests

1 participant