为 Sequelize ORM 配置 babel-typescript 会导致未定义的属性 [英] Configuring babel-typescript for Sequelize ORM causes undefined properties

查看:9
本文介绍了为 Sequelize ORM 配置 babel-typescript 会导致未定义的属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

sequelize-typescriptbabel-preset-typescript.列应通过 对象检索.defineProperty() 但是当现有属性已经存在时,它会被跳过 在这里查看源代码.当使用 Sequelize† 运行常规 typescript 实现的代码时,一切正常,但在实现 babel-typescript 时,属性仍然存在.

There is a problem when using sequelize-typescript together with babel-preset-typescript. Columns should be retrieved through the Object.defineProperty() but when an existing property already exists it is skipped see source here. When running the code with regular typescript implementation with Sequelize† everything works but when implementing babel-typescript the properties remain.

问题:我应该如何定义我的 babel 配置,以便正确忽略这些属性?

Question: How should I define my babel configuration so that the properties are properly ignored?

我尝试了两种 Babel 定义,一种具有所有所需的功能和一个需要明确定义的.

I've tried two Babel definitions, one that has all desired features & one that requires explicit definitions.

module.exports = {
  plugins: [
    [
      "@babel/plugin-transform-runtime",
      {
        regenerator: true,
      },
    ],
    "babel-plugin-inline-import",
    "babel-plugin-transform-typescript-metadata",
    ["@babel/plugin-proposal-decorators", { legacy: true }],
    ["@babel/proposal-class-properties", { loose: true }],
    "@babel/proposal-object-rest-spread",
    "@babel/plugin-proposal-optional-chaining",
    [
      "module-resolver",
      {
        alias: {
          "~": "./src",
        },
      },
    ],
  ],
  presets: ["@babel/preset-env", "@babel/typescript"],
};

微小的显式定义

module.exports = {
  plugins: [
    ["@babel/proposal-decorators", { legacy: true }],
    ["@babel/proposal-class-properties", { loose: true }],
    "@babel/proposal-async-generator-functions",
    "@babel/proposal-object-rest-spread",
    "@babel/plugin-transform-runtime",
  ],
  presets: ["@babel/preset-env", "@babel/typescript"],
};

背景与详情

您可以在这里找到一个用于测试的常规非 babel 示例,分支 babel_buildbabel_explicit 包含不同的 babel 实现.

Background & details

You can find a regular non-babel example for testing here, the branch babel_build and babel_explicit contain different babel-implementations.

定义表的要点是在代码中添加装饰器的一个小例子:

The gist of defining a table is a trivial case of adding decorators to the code:

import { Table, Column, Model } from 'sequelize-typescript'

@Table
class Person extends Model {
  @Column
  name: string

  @Column
  birthday: Date
}

问题是,如果我们通过以下方式检索一个案例:

The problem is that if we then retrieve a case through:

const p = await Person.findOne({ name: "John Doe" })
console.log(p)

我们得到:

Person {
 dataValues: {
   id: 1,
   name: "John Doe",
   birthday: null
 },
  _previousDataValues: {
   id: 1,
   name: "John Doe",
   birthday: null
 },
  _changed: Set(0) {},
  _options: {
    isNewRecord: false,
    _schema: null,
    _schemaDelimiter: '',
    raw: true,
    attributes: [
      'id',
      'name',
      'birthday'
    ]
  },
  isNewRecord: false,
  name: undefined,
  birthday: undefined,
}

undefined 记下最后两行.

似乎正确的定义应该使用 declare 关键字.虽然这应该可以解决问题,但它在实践中不起作用.

It seems that the correct definitions should be using the declare keyword. While this should solve the issue it doesn't work in practice.

import { Table, Column, Model } from 'sequelize-typescript'

@Table
class Person extends Model {
  @Column
  declare name: string

  @Column
  declare birthday: Date
}

配置:

module.exports = {
  plugins: [
    ["@babel/plugin-transform-typescript", { allowDeclareFields: true }],
    [
      "@babel/plugin-transform-runtime",
      {
        regenerator: true,
      },
    ],
    "babel-plugin-transform-typescript-metadata",
    ["@babel/plugin-proposal-decorators", { legacy: true }],
    ["@babel/proposal-class-properties", { loose: true }],
    [
      "module-resolver",
      {
        alias: {
          "~": "./src",
        },
      },
    ],
  ],
  presets: ["@babel/preset-env"],
};

很遗憾,问题没有解决,但似乎 declare 更符合预期用途.

Unfortunately, the issue is not resolved but seems that declare is more in line with the intended use.

† 这很可能独立于 sequelize-typescript 附加组件

† this is most likely independent of the sequelize-typescript add-ons

推荐答案

来自 https://sequelize.org/master/manual/typescript.html,模型初始化的TypeScript定义是正确的,但是文档不正确!

From https://sequelize.org/master/manual/typescript.html, the TypeScript definition for model initialization is correct, but the documentation is not correct!

当你使用 null 断言时 (public id!: number;//注意null assertion"; !"在严格模式下是必需的),这会使属性在实例初始化时为 undefined.

When you use the null assertion (public id!: number; // Note that the "null assertion" "!" is required in strict mode), this make the attribute as undefined at instance initialization.

我的想法是在 Model Constructor 使用 getDataValue() 强制模型初始化,并避免使用推荐的 null 断言.p>

示例

My idea is to force the model initialization using getDataValue() at Model Constructor and avoid to use the recommended null assertion.

import {
  BuildOptions,
  Sequelize,
  Model,
  Optional,
} from "sequelize";

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

// These are all the attributes in the User model
interface UserAttributes {
  id: number;
  name: string;
  //preferredName!: string | null;
  preferredName: string | null;
}

// Some attributes are optional in `User.build` and `User.create` calls
interface UserCreationAttributes extends Optional<UserAttributes, "id"> {}

class User extends Model<UserAttributes, UserCreationAttributes>
  implements UserAttributes {
  //public id!: number; // Note that the `null assertion` `!` is required in strict mode.
  public id: number;

  //public name!: string;
  public name: string;

  //public preferredName!: string | null; // for nullable fields
  public preferredName: string | null;

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

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

  /**
   * Initialization to fix Sequelize Issue #11675.
   *
   * @see https://stackoverflow.com/questions/66515762/configuring-babel-typescript-for-sequelize-orm-causes-undefined-properties
   * @see https://github.com/sequelize/sequelize/issues/11675
   * @ref #SEQUELIZE-11675
   */
  constructor(values?: TCreationAttributes, options?: BuildOptions) {
    super(values, options);

    // All fields should be here!
    this.id = this.getDataValue('id');
    this.name = this.getDataValue('name');
    this.preferredName = this.getDataValue('preferredName');
    this.createdAt = this.getDataValue('createdAt');
    this.updatedAt = this.getDataValue('updatedAt');
  }
}

这篇关于为 Sequelize ORM 配置 babel-typescript 会导致未定义的属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆