-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Repo class factory improvements #4949
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
Conversation
|
@bajtos @raymondfeng , |
| ); | ||
| const AddressRepository = defineCrudRepositoryClass< | ||
| typeof Address, | ||
| DummyCrudRepository<Address> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So TypeScript cannot infer the generic types from the parameters in this case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To make inference happy, I have to do the following:
function castDummyRepository<M extends Model>() {
return class extends DummyCrudRepository<M> {};
}
const AddressRepository = defineCrudRepositoryClass(
Address,
castDummyRepository<Address>(),
);There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TypeScript does not allow typeof DummyCrudRepository<M>.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So TypeScript cannot infer the generic types from the parameters in this case?
Unfortunately not. I think we should report a bug to TypeScript (if it wasn't already reported).
To make inference happy, I have to do the following (...)
IMO, castDummyRepository does not solve the problem of people forgetting to specify the repository type and relying on the compiler to infer it. Once they realize some extra effort is needed, then I think it's best to provide generic type parameters and there is no need to introduce cast*Repository functions.
|
|
||
| const ProductRepository = defineKeyValueRepositoryClass< | ||
| typeof Product, | ||
| MyKeyValueRepo<Product> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same question here.
@emonddr I am little bit confused, how is this new code related to |
bajtos
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking about this (and your) pull request last week and I think we have over-engineered this feature. I see two main use cases we need to support:
- The user wants to define a new repository class extending their custom repository base class.
- The user wants an easy way how to create a model-bound repository using the built-in CRUD or KeyValue implementation.
If they want to use a custom repository extending DefaultCrudRepository base class, then there is no need to have a specialized defineEntityCrudRepository helper, because they can use the generic helper created to support the first use case in my list above.
With that in mind, I added a new commit 078dfba to greatly simplify the new API.
@raymondfeng PTAL at my proposed version and also at my comments below 👇
| * @typeParam R - Repository class/interface | ||
| */ | ||
| export interface CrudRepositoryClass< | ||
| export interface NamedRepositoryClass< |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no need to have specialized types for KeyValue and CRUD repositories, we can use a generic Repository type as the starting point.
I am not comfortable using RepositoryClass name, because it's too ambiguous and possibly confusing, since it uses different constructor argument than PersistedModelClass and friends.
I was looking for a name that would describe the fact that the repository class is already bound to a given model. ModelRepositoryClass did not feel right either, so I settled on NamedRepositoryClass. Feel free to propose a better name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it's NamedRepositoryClass, should we add name: string explicitly?
What about GeneratedRepositoryClass?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should take another angle. My intention is to distinguish between a generic repository class that works for any Model/Entity and a repository class that's already specialized for (bound to) a given Model/Entity.
In that light, perhaps BoundRepositoryClass or SpecializedRepositoryClass would be better names?
| */ | ||
| new ( | ||
| // Make model class conditional of Model or Entity | ||
| modelClass: (M extends Entity ? typeof Entity : typeof Model) & { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While this works for Entities, it won't work for other subclasses that people may use for their base repository classes.
Consider the following:
class MyAuditedEntityRepository<E extends AuditedEntity, /*...�*/> extends DefaultCrudRepository<E, /*...*/> {
constructor(modelClass: typeof AuditedEntity, dataSource: juggler.dataSource) {
// ...
}
// ...
}To support such use case, I think it's best to use M extends typeof Model as the generic type parameter.
| M extends Model, | ||
| R extends CrudRepository<M> | ||
| export function defineRepositoryClass< | ||
| M extends typeof Model, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similarly here, we need M to represent the class constructor, not the class instance.
Instead of accepting Model/Entity instance and dealing with Constructor<M> complexity, which requires additional type casts, I am proposing to rework `BaseRepositoryClass` to accept `typeof Model` instead. This way the generic argument can capture: 1. class constructor 2. static members 3. prototype (instance) members Signed-off-by: Miroslav Bajtoš <mbajtoss@gmail.com>
Signed-off-by: Miroslav Bajtoš <mbajtoss@gmail.com>
Signed-off-by: Miroslav Bajtoš <mbajtoss@gmail.com>
078dfba to
b8e6a04
Compare
|
@bajtos I have rebased this branch against latest master. |
ab4950a to
3ec19d0
Compare
3ec19d0 to
401a81c
Compare
|
Merged manually. |
A proposal how to improve several aspects of #4792.
@raymondfeng if you accept my suggestions, then please preserve my co-authorship in the git commit message (see https://github.blog/2018-01-29-commit-together-with-co-authors/)