themetalfleece / neogma

Object-Graph-Mapping neo4j framework, Fully-typed with TypeScript, for easy and flexible node and relationship operations
https://themetalfleece.github.io/neogma/
MIT License
124 stars 12 forks source link

How to define type of custom method #36

Closed JonasHiltl closed 3 years ago

JonasHiltl commented 3 years ago

Below is a model im trying to create with a custom method called getPublicUser. It should just be a simple method that returns the public information of a user. But I'm having issues with defining the type of the method because I always get the typescript error:

Type 'UserDoc' does not satisfy the constraint 'Record<string, string | number | boolean | Integer | Point<Integer> | Date<Integer> | Time<Integer> | LocalTime<Integer> | ... 4 more ... | undefined>'.
  Index signature for type 'string' is missing in type 'UserDoc'.

I don't quite understand this error but my guess would be that the constraint for the generic of the ModelFactory does not allow a function to be defined, only Record<string, string | number | boolean | Integer | Point<Integer> | Date<Integer> | Time<Integer> | LocalTime<Integer> | ... 4 more ... | undefined>.

This should either be a bug or I just declared the type of the method at the wrong place.

type IUser = {
    phone?: string;
    phoneVerified?: boolean;
    lastname?: string;
    picture?: string;
    id: string;
    username: string;
    email: string;
    emailVerified: boolean;
    firstname: string;
    password: string;
    role: Role;
}

 UserRelations {}

interface UserDoc extends IUser {
  methods: {
    getPublicUser: () => IPublicUser;
  };
}

export const User = ModelFactory<UserDoc, UserRelations>(
  {
    label: "User",
    primaryKeyField: "id",
    schema: {
      id: {
        type: "string",
        required: true,
        minLength: 1,
        conform: (v) => uuid.validate(v),
      },
      username: {
        type: "string",
        required: true,
      },
      firstname: {
        type: "string",
        required: true,
      },
      lastname: {
        type: "string",
      },
      email: {
        type: "string",
        required: true,
        pattern: EMAIL_REGEX,
      },
      emailVerified: {
        type: "boolean",
        default: false,
        required: true,
      },
      phone: {
        type: "string",
      },
      phoneVerified: {
        type: "boolean",
        default: false,
      },
      picture: {
        type: "string",
      },
      password: {
        type: "string",
      },
      role: {
        type: "string",
        enum: ["USER", "DEV", "ADMIN"],
        required: true,
      },
    },
    methods: {
      getPublicUser: function (): IPublicUser {
        return {
          id: this.id,
          username: this.username,
          firstname: this.firstname,
          lastname: this.lastname,
          email: this.email,
          role: this.role,
          phone: this.phone,
          picture: this.picture,
        };
      },
    },
  },
  neogma
);

User.beforeCreate = async (user) => {
  user.password = await hash(user.password, SALT_ROUNDS);
};
themetalfleece commented 3 years ago

Hey Jonas,

Please have a look here (press the "Typescript" tab). You need to something in the lines of:

type IUser = {
    phone?: string;
    phoneVerified?: boolean;
    ...
}

// default value if it's empty
type UserRelations = Object;

interface IUserMethods {
    getPublicUser: (this: UserInstance) => IPublicUser;
}

type IUserStatics = Object;

// import NeogmaInstance
type UserInstance = NeogmaInstance<IUser, UserRelations, IUserMethods>;

export const User = ModelFactory<UserInstance, UserRelations, IUserStatics, IUserMethods>(
    // do not define any methods inside "User"
    ...
}

// the following should have propert typing for for "this" and the return value
User.prototype.getPublicUser = function () {
    // method body here
}

Please me know if you got it working

JonasHiltl commented 3 years ago

Oh, that makes sense, I never clicked the typescript tab. That's awesome. Thanks a lot for your work creating this library.