Doubt about generic types in Typescript

Asked

Viewed 109 times

5

I am currently studying Typeorm, and wanted to create a generic controller, as it will always be the same CRUD operations. I know I can solve with native Typeorm solutions, such as getRepository(), I have even resolved it, but now I want to remove this doubt that persists...

So, in the way I tried earlier, my entities are extending the class BaseEntity, to be able to access the method getRepository() directly in the class. So:

export abstract class AbstractEntity extends BaseEntity {
    @PrimaryGeneratedColumn()
    id: number;

    @Column({default: true})
    ativo: boolean;

    @CreateDateColumn()
    createdAt: string;

    @UpdateDateColumn({ type: "timestamp" })
    updatedAt: number;
}

And this class just to illustrate:

export class Pessoa extends AbstractEntity {
    @Column()
    nome: string;
}

I was hoping to create a generic controller more or less like this:

class GenericController<T extends BaseEntity> {
        async getAll(request: Request, response: Response, nextFunction: NextFunction) {
            return await T.getRepository().find();
        }
    }

To use thus:

export default class PessoaRoute {
    pessoaController = new GenericController<Pessoa>()

    constructor() {
        this.initializeRoutes();
    }

    initializeRoutes() {
        this.router.get('/', this.pessoaController.getAll);
    }
}

The problem I have is that in GenericController I don’t have access to the methods I need through T, not even it is recognized as existing in Typescript, that in that mode I intend to use: T.getRepository().

I read a little in the Typescript documentation but I didn’t understand why it didn’t work.

1 answer

6


The problem you are facing boils down to: the generic never is a value, but rather a type. That way, you cannot use it as a value.

See, in your code, you’re doing this:

return await T.getRepository().find();

You see? You’re treating the generic type T as if it were a value, which it is not.

So what you need to do is accept the object Pessoa as class argument (probably in the constructor). Something like this:

class GenericController<T extends Entity> {
  constructor(private model: T) { }

  async getAll(/* Seus parâmetros aqui... */) {
    return this.model.getRepository().find();
  }
}

Note that we are now using this.model, which is a value with the type T.

Then to use, just do this:

export default class PessoaRoute {
  pessoaController = new GenericController(Pessoa);

  // ...
}

Note that it is not necessary to explicitly pass the generic in GenericController, since Typescript will automatically infer the type.


It doesn’t have much to do with the question, but note that I removed the await of return await. In that situation, the return await is high redundant. The very async in that function is also not necessary, but to make it explicitly asynchronous, it is a good practice to leave it there. :)

Browser other questions tagged

You are not signed in. Login or sign up in order to post.