question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

undefined attributes following typescript example

See original GitHub issue

Issue Description

What are you doing?

// this is a copy of https://sequelize.org/master/manual/typescript.html
import { Sequelize, Model, DataTypes, BuildOptions } from 'sequelize';
import { HasManyGetAssociationsMixin, HasManyAddAssociationMixin, HasManyHasAssociationMixin, Association, HasManyCountAssociationsMixin, HasManyCreateAssociationMixin } from 'sequelize';

class User extends Model {
  public id!: number; // Note that the `null assertion` `!` is required in strict mode.
  public name!: string;
  public preferredName!: string | null; // for nullable fields

  // timestamps!
  public readonly createdAt!: Date;
  public readonly updatedAt!: Date;

  // Since TS cannot determine model association at compile time
  // we have to declare them here purely virtually
  // these will not exist until `Model.init` was called.

  public getProjects!: HasManyGetAssociationsMixin<Project>; // Note the null assertions!
  public addProject!: HasManyAddAssociationMixin<Project, number>;
  public hasProject!: HasManyHasAssociationMixin<Project, number>;
  public countProjects!: HasManyCountAssociationsMixin;
  public createProject!: HasManyCreateAssociationMixin<Project>;

  // You can also pre-declare possible inclusions, these will only be populated if you
  // actively include a relation.
  public readonly projects?: Project[]; // Note this is optional since it's only populated when explicitly requested in code

  public static associations: {
    projects: Association<User, Project>;
  };
}

const sequelize = new Sequelize('mysql://root:asd123@localhost:3306/mydb');

class Project extends Model {
  public id!: number;
  public ownerId!: number;
  public name!: string;

  public readonly createdAt!: Date;
  public readonly updatedAt!: Date;
}

class Address extends Model {
  public userId!: number;
  public address!: string;

  public readonly createdAt!: Date;
  public readonly updatedAt!: Date;
}

Project.init({
  id: {
    type: DataTypes.INTEGER.UNSIGNED, // you can omit the `new` but this is discouraged
    autoIncrement: true,
    primaryKey: true,
  },
  ownerId: {
    type: DataTypes.INTEGER.UNSIGNED,
    allowNull: false,
  },
  name: {
    type: new DataTypes.STRING(128),
    allowNull: false,
  }
}, {
  sequelize,
  tableName: 'projects',
});

User.init({
  id: {
    type: DataTypes.INTEGER.UNSIGNED,
    autoIncrement: true,
    primaryKey: true,
  },
  name: {
    type: new DataTypes.STRING(128),
    allowNull: false,
  },
  preferredName: {
    type: new DataTypes.STRING(128),
    allowNull: true
  }
}, {
  tableName: 'users',
  sequelize: sequelize, // this bit is important
});

Address.init({
  userId: {
    type: DataTypes.INTEGER.UNSIGNED,
  },
  address: {
    type: new DataTypes.STRING(128),
    allowNull: false,
  }
}, {
  tableName: 'address',
  sequelize: sequelize, // this bit is important
});

// Here we associate which actually populates out pre-declared `association` static and other methods.
User.hasMany(Project, {
  sourceKey: 'id',
  foreignKey: 'ownerId',
  as: 'projects' // this determines the name in `associations`!
});

Address.belongsTo(User, {targetKey: 'id'});
User.hasOne(Address,{sourceKey: 'id'});

async function stuff() {
  // Please note that when using async/await you lose the `bluebird` promise context
  // and you fall back to native
  const newUser = await User.create({
    name: 'Johnny',
    preferredName: 'John',
  });
  console.log(newUser.id, newUser.name, newUser.preferredName);

  const project = await newUser.createProject({
    name: 'first!',
  });

  const ourUser = await User.findByPk(1, {
    include: [User.associations.projects],
    rejectOnEmpty: true, // Specifying true here removes `null` from the return type!
  });
  console.log(ourUser.projects![0].name); // Note the `!` null assertion since TS can't know if we included
                                          // the model or not
}

My tsconfig:

"compilerOptions": {
    "outDir": "dist",
    "sourceMap": false,
    "allowJs": true,
    "noUnusedLocals": true,
    "noUnusedParameters": false,
    "resolveJsonModule": true,
    "module": "commonjs",
    "strictNullChecks": true,
    "allowSyntheticDefaultImports": true,
    "lib": ["es7", "es2015", "dom"],
    "target": "es2015",
    "baseUrl": "."
  }

What do you expect to happen?

I expect ourUser.projects to be defined

What is actually happening?

console.log(ourUser.projects) // undefined

Context

Note that I also observe some strange behavior: when I add the flag raw: true in the query, the result looks like this:

{ 
  id: 1,
  createdAt: 2019-11-06T08:35:49.172Z,
  updatedAt: 2019-11-06T08:35:49.172Z,
  'projects.id': 1,
  'projects.ownerId': 1,
  'projects.createdAt': 2019-11-08T12:41:40.520Z,
  'projects.updatedAt': 2019-11-08T15:32:42.822Z
}

Environment

  • Sequelize version: 5.21.2
  • Node.js version: v10.16.3
  • Operating System: OSX
  • TypeScript version: 3.6.3

Issue Template Checklist

How does this problem relate to dialects?

  • I think this problem happens regardless of the dialect.
  • I think this problem happens only for the following dialect(s):
  • I don’t know, I was using PUT-YOUR-DIALECT-HERE, with connector library version XXX and database version XXX

Would you be willing to resolve this issue by submitting a Pull Request?

  • Yes, I have the time and I know how to start.
  • Yes, I have the time but I don’t know how to start, I would need guidance.
  • No, I don’t have the time, although I believe I could do it if I had the time…
  • No, I don’t have the time and I wouldn’t even know how to start.

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:5
  • Comments:9 (3 by maintainers)

github_iconTop GitHub Comments

3reactions
nicgiraultcommented, Nov 14, 2019

For French readers: this post helped me understand what is happening: https://openclassrooms.com/forum/sujet/proprietes-de-classe-avec-node-12-babel. It seems there is a conflict with babel class properties. Migrating to node v12 and removing babel plugin @babel/proposal-class-properties solved my issue

2reactions
ephyscommented, Jan 2, 2022
Read more comments on GitHub >

github_iconTop Results From Across the Web

Documentation - TypeScript 2.0
Null- and undefined-aware types. TypeScript has two special types, Null and Undefined, that have the values null and undefined respectively.
Read more >
How to Deal with Optional Things and "Undefined" in TypeScript
Working with JavaScript means working with undefined . It's a standard way to say, “This thing you asked for doesn't exist.”.
Read more >
Undefined value after assignment in Typescript - Stack Overflow
The model is undefined after the assignment because it is looking for a Pascal-cased property in the JSON, which doesn't exist, for example:....
Read more >
How to Avoid the Infamous "Cannot read properties of ... - Bitovi
With TypeScript, there are two ways of interpreting null and undefined types and one of them can avoid the 'Cannot read properties of ......
Read more >
TypeScript Null & Undefined - W3Schools
Optional Chaining is a JavaScript feature that works well with TypeScript's null handling. It allows accessing properties on an object, that may or...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found