sequelize-typescript icon indicating copy to clipboard operation
sequelize-typescript copied to clipboard

Intance model properties are undefined

Open gideaoms opened this issue 4 years ago • 20 comments

Versions

  • sequelize: 5.21.7
  • sequelize-typescript: 1.1.0
  • typescript: 3.8.3

I'm submitting a ...

[ x ] bug report [ ] feature request

Actual behavior: I execute a findOne function to get a model instance, but the properties are undefined

Expected behavior: I would like to get the properties values

Steps to reproduce:

const city = await CityModel.findOne({ where: { name: 'example' } })
console.log(city.name);

Related code: If I execute the city.getDataValue('name') I see the right value, but simply running city.name I get undefined.

If I execute console.log(city) all works well:

CityModel {
    dataValues: {
        id: 'fb73e40a-9055-42b8-9a21-d8368ef13222',
        name: 'Example',
        createdAt: 2020-04-27,
        updatedAt: 2020-04-27
    },
    _previousDataValues: {
        id: 'fb73e40a-9055-42b8-9a21-d8368ef13222',
        name: 'Example',
        createdAt: 2020-04-27,
        updatedAt: 2020-04-27
    },
    ...
},

My model:

import {
  Model,
  Table,
  DataType,
  Column,
  PrimaryKey,
  Default,
  HasMany,
} from 'sequelize-typescript';
import UserModel from './user.model';

@Table({ tableName: 'Cities', timestamps: true })
class CityModel extends Model<CityModel> {
  @PrimaryKey
  @Default(DataType.UUIDV4)
  @Column(DataType.UUIDV4)
  id!: string;

  @Column(DataType.STRING)
  name!: string;

  @Column(DataType.STRING)
  initials!: string;

  @Column(DataType.STRING)
  status!: string;

  @HasMany(() => UserModel, 'cityId')
  users!: UserModel[];
}

export default CityModel;

gideaoms avatar Apr 27 '20 21:04 gideaoms

Same problem. It seems like I need to call getDataValue.

wdzeng avatar Jan 13 '21 06:01 wdzeng

I created a quick repo here showing the issue: https://github.com/jrm2k6/laughing-octo-enigma

When running the command to execute a findAll this is what I get back:

Executing (default): SELECT "uuid", "name", "createdAt", "updatedAt", "deletedAt" FROM "users" AS "users" WHERE ("users"."deletedAt" IS NULL);
[
  users {
    dataValues: {
      uuid: '36497139-77d2-4edb-ba49-481a589f2f8c',
      name: 'John Smith',
      createdAt: 2021-02-22T01:15:35.993Z,
      updatedAt: null,
      deletedAt: null
    },
    _previousDataValues: {
      uuid: '36497139-77d2-4edb-ba49-481a589f2f8c',
      name: 'John Smith',
      createdAt: 2021-02-22T01:15:35.993Z,
      updatedAt: null,
      deletedAt: null
    },
    _changed: {},
    _modelOptions: {
      timestamps: true,
      validate: {},
      freezeTableName: false,
      underscored: false,
      paranoid: true,
      rejectOnEmpty: false,
      whereCollection: null,
      schema: null,
      schemaDelimiter: '',
      defaultScope: {},
      scopes: {},
      indexes: [],
      name: [Object],
      omitNull: false,
      sequelize: [Sequelize],
      hooks: {}
    },
    _options: {
      isNewRecord: false,
      _schema: null,
      _schemaDelimiter: '',
      raw: true,
  1 node_modules/
      attributes: [Array]
    },
    isNewRecord: false,
    uuid: undefined,
    name: undefined
  }
]

I also use sequelize-typescript with nestjs and I am not having the same issue but haven't found out if it does something different.

Hope that helps so that we can figure out if we are doing something wrong or not.

Thanks!

jrm2k6 avatar Feb 22 '21 01:02 jrm2k6

@jrm2k6 The problem is I can not get the value just using user.name, but if I use user.getDataValue('name') works fine.

I tested it with your github project and the result was the same.

gideaoms avatar Feb 22 '21 13:02 gideaoms

@gideaoms Yes, this is why I added a way for anybody to get the same results as we have and see if it is something wrong with our setup or not. Makes it easier for a maintainer to understand the issue and be able to debug if needed.

jrm2k6 avatar Feb 22 '21 14:02 jrm2k6

Ok I did some more investigating and I think in my example, it boils down to using babel-node vs ts-node. In here: https://github.com/jrm2k6/laughing-octo-enigma/commit/e745244c1b995f174adf2d48263b30ac52c49e3c I changed my tsconfig to use commonjs instead of esnext, and use ts-node instead of babel-node.

When I run node -r ts-node/register index.ts it lets me access the model properties directly. I am not 100% sure yet but I think with the way my babel-node config is set up, it will generate model properties automatically initialized with undefined, overriding what sequelize-typescript might be doing when it comes to accessing model properties. Still a guess though.

Here we go: https://github.com/RobinBuschmann/sequelize-typescript/issues/612

jrm2k6 avatar Feb 28 '21 17:02 jrm2k6

Yep, it makes sense.

gideaoms avatar Mar 01 '21 19:03 gideaoms

Encountered this issue when upgrading typescript from 3 to 4. Data was only in dataValues but not in the class fields. Setting useDefineForClassFields to true in my tsconfig seems to have fixed it.

triesmon avatar Sep 23 '21 13:09 triesmon

That's weird. For me doing the exact opposite (setting useDefineForClassFields to false) fixed it. I'm targeting ESNext on Node 14.

nathanlepori avatar Sep 30 '21 20:09 nathanlepori

I had actually set it to false but just wrote the wrong value in the comment. Thanks.

triesmon avatar Sep 30 '21 20:09 triesmon

For me, it had nothing to do with babel (which I wasn't using to begin with).

The solution was entirely to add "useDefineForClassFields": "false" to my tsconfig.json.

Thanks, @triesmon!

devuxer avatar Oct 19 '21 01:10 devuxer

This appears to be the change (and some surrounding discussion) in babel that breaks this behavior:

https://github.com/babel/babel/pull/11747

tommoor avatar Jan 07 '22 01:01 tommoor

Take a look at the updated sequelize documentation regarding the issue with class fields: https://sequelize.org/v6/manual/model-basics.html#caveat-with-public-class-fields

ephys avatar Jan 07 '22 16:01 ephys

The new documentation is a big improvement over where we were at a week ago, unfortunately the declare keyword trick only works with tsc tooling and not with babel due to the above linked PR.

tommoor avatar Jan 07 '22 16:01 tommoor

The fact that babel still emits public class fields tagged with declare when using decorators is problematic. Makes sequelize/sequelize#14300 all the more pressing.

ephys avatar Jan 07 '22 17:01 ephys

I have worked around this in my project with a model class decorator – it's very ugly, but the approach could be used as another way to fix the issue more formally in sequelize by applying the getter/setters after the model constructor.

https://github.com/outline/outline/blob/main/server/models/decorators/Fix.ts

tommoor avatar Jan 07 '22 18:01 tommoor

I had this problem as well in Deno 1.32 which doesn't support useDefineForClassFields. I fixed it by just declare-ing all my fields:

...

  @Column({
    allowNull: false,
    defaultValue: "",
  })
  declare myField: string

keysmusician avatar Apr 28 '23 09:04 keysmusician

First of all great project! Strange that README.md not updated with 'declare' workaround, as well as with ReturnType<() => Model>; for associated fields. To prepare PR with "declare" column attributes and "ReturnType" associated attributes?

d00rsfan avatar Jul 05 '23 08:07 d00rsfan