Skip to content

Generics for the model attributes? #125

Open

Description

@rokoroku

Why don't we have generic for the model attributes?
e.g.

interface TypedModelAttributes {
  id: number;
  optionalData?: object;
}

class TypedModel extends Model<TypedModelAttributes> {
  ...
}

// usage
TypedModel.build({ ... }) // will be type-checked by "TypedModelAttributes"
TypedModel.findOne({ where: { ... }) // will be type-checked by "keyof TypedModelAttributes"
...

It is more accurate and will bring a lot of conveniences than current <K extends keyof this> way.

Activity

  1. felixfbecker commented on Sep 5, 2017

    @felixfbecker
    Collaborator

    Why do you think is it more accurate than this?

    class TypedModel extends Model {
      public id: number;
      public optionalData?: object;
    }

    The attributes are attributes of the class, so imo they should be declared there.
    What conveniences will it bring exactly?

  2. rokoroku commented on Sep 5, 2017

    @rokoroku
    ContributorAuthor

    when we define custom model method

    class TypedModel extends Model {
      public id: number;
      public optionalData?: object;
      aMethod() {
        ...
      }
      anotherMethod() {
        ...
      }
    }

    aMethod and anotherMethod can be treated as model's attributes in the <K extends keyof this> scenario.

    Furthermore, the model prototype's own method (such as get, set, toJSON, ... ) also treated as their attributes, so it makes the intellisense (quick suggestions) bulky with unnecessary values.

  3. SimonSchick commented on Sep 11, 2017

    @SimonSchick
    Contributor

    Making the attributes a model would also properly restrict attributes: [...], and allow .get() to return correct objects.

  4. felixfbecker commented on Sep 11, 2017

    @felixfbecker
    Collaborator

    I don't think it's that big of an issue that you can also pass the methods. I don't think that would cause any error in real-world usage. On the other hand, having to duplicate the attribute declaration in both the class and an interface is cumbersome.

  5. rokoroku commented on Sep 11, 2017

    @rokoroku
    ContributorAuthor

    Though writing same attribute declaration in class and interface is bit cumbersome, it's not a requirement. We can also write a model without generic thanks to the TypeScripts' default type parameters.

    Like @SimonSchick said, generic attributes will provide more help when we write where or attributes clause, also allowing any sequelize.fn type too.

    If you don't mind, I'll try to make a suggestion PR from below scratch.

    type WhereAttributeHash<TAttributes = any> = {
      [K in keyof TAttributes]?: TAttributes[K] | WhereValue<TAttributes[K]>;
    };
    
    type WhereValue<TAttributes = any> =
      TAttributes
      | null
      | WhereOperators<TAttributes>
      | WhereAttributeHash<TAttributes> // for JSON columns
      | Col<TAttributes> // reference another column
      | OrOperator<TAttributes>
      | AndOperator<TAttributes>
      | WhereGeometryOptions<TAttributes>
      | Array<TAttributes | WhereAttributeHash<TAttributes>>; // implicit $or
    
    type WhereOptions<T = any> = WhereAttributeHash<T> | null | T | T[];
  6. SimonSchick commented on Sep 24, 2017

    @SimonSchick
    Contributor

    One way to achieve this without massive duplication would be:

    type Constructor<T> = new (...args: any[]) => T;
    
    function mixin<TI, T extends typeof Mode l= typeof Model>(cls: T): T & Constructor<TI> {
      return <T & Constructor<TI>> <any> cls;
    }
    
    export interface RoleProperties {
      userName: string;
    }
    
    // tslint:disable-next-line:max-classes-per-file
    export class Role extends mixin<RoleProperties>(Model) {
    ...
    }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions