rockets icon indicating copy to clipboard operation
rockets copied to clipboard

Improve userMutateService to Support Generics for Dynamic Properties

Open tnramalho opened this issue 1 year ago • 5 comments

Use case

Feature Request: Improve userMutateService to Support Generics for Dynamic Properties

Description

Currently, the userMutateService in the Rockets library is tightly coupled with predefined interfaces. This limitation prevents the addition of new properties to the user entity without encountering TypeScript errors.

Today we need to create a new mutate service with all new DTOs and interfaces for each update, which is inefficient and cumbersome.

Code Example to Reproduce the Error

Step 1: Define the User Entity

// packages/rockets-api/src/entities/user.entity.ts
export class UserEntity extends UserPostgresEntity {
  // New property to be added
  @Column({ nullable: true })
  newProperty?: string;
}

Step 3: Attempt to Update User with New Property

// packages/rockets-api/src/my.controller.ts
await this.userMutateService.update({
  id: user.id,
  newProperty: 'some value', // Error occurs here
});
Object literal may only specify known properties, and ‘newProperty’ does not exist in type ‘UserUpdatableInterface & ReferenceIdInterface<string>’.ts(2353)

Proposal

Proposed Solution

  1. Introduce Generics: Modify the userMutateService to accept generics that allow for the inclusion of additional properties in the update process. This would enable developers to extend the existing interfaces without needing to create entirely new services.

  2. Update Interfaces: Adjust the UserUpdatableInterface to be more flexible, allowing for optional properties that can be defined at the time of the update.

  3. Example Implementation:

    • Update the update method in the userMutateService to accept a generic type parameter.
    • Ensure that the method can handle additional properties without throwing TypeScript errors.

Benefits

  • Flexibility: Developers can easily add new properties to the user entity without the need for extensive refactoring.
  • Maintainability: Reduces the need for multiple services for similar functionalities, making the codebase cleaner and easier to maintain.
  • Type Safety: By using generics, we can maintain type safety while allowing for dynamic properties.

Additional Notes

This improvement would significantly enhance the usability of the userMutateService and streamline the process of updating user entities in the application.

tnramalho avatar Feb 07 '25 21:02 tnramalho

This can't be done with generics on the method. You will have to extend the service and pass the overriding generics to the class declaration. These generics are currently missing on all lookup and mutate services.

MrMaz avatar Feb 21 '25 22:02 MrMaz

This is already supported if you pass a DTO.

interface MyUserCreateInterface extends UserCreatableInterface {
  newProp?: string;
}

class MyUserCreateDto extends UserCreateDto implements MyUserCreateInterface {
  newProp?: string;
}

const foo = new UserMutateService(/* define services */);

async function createUserOK() {
  const dto = new MyUserCreateDto({
    username: 'Foo',
    email: '[email protected]',
    newProp: 'hello', // <-- OK
  });
  return foo.create(dto);
}

async function createUserBad() {
  return foo.create({
    username: 'Foo',
    email: '[email protected]',
    newProp: 'hello', // <-- error
  });
}

I played around with the typing, and it will be very difficult to support object literal value for the parameter. Maybe there is a chance we can use it to support return type.

MrMaz avatar Feb 24 '25 23:02 MrMaz

if on that case would the newProp be validated from class-validator?

tnramalho avatar Feb 27 '25 16:02 tnramalho

if on that case would the newProp be validated from class-validator?

i'm pretty sure, please run a test on your local to validate

MrMaz avatar Feb 27 '25 22:02 MrMaz

@tnramalho please test on your local so we can work on this or close.

MrMaz avatar Mar 04 '25 14:03 MrMaz