sequelize.js - Konfigurering av babel-typescript for Sequelize ORM forårsaker udefinerte egenskaper
Det oppstår et problem når man bruker sequelize-typescript sammen med babel-preset-typescript. Kolonner bør hentes gjennom Object.defineProperty(), men når en eksisterende egenskap allerede finnes, blir den hoppet over, se kilde her. Når koden kjøres med vanlig TypeScript-implementering med Sequelize†, fungerer alt, men når man implementerer babel-typescript, forblir egenskapene.
Spørsmål: Hvordan bør jeg definere min Babel-konfigurasjon slik at egenskapene blir korrekt ignorert?
Babel-definisjoner (babel.config.js)
Jeg har prøvd to Babel-definisjoner, en som har alle ønskede funksjoner og en som krever eksplisitte definisjoner.
Full definisjon
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"],
};
Liten eksplisitt definisjon
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"],
};
Bakgrunn og detaljer
Du kan finne et vanlig ikke-babel-eksempel for testing her, grenene babel_build og babel_explicit inneholder forskjellige babel-implementasjoner.
Hovedpoenget med å definere en tabell er et trivielt tilfelle av å legge til dekoratører i koden:
import { Table, Column, Model } from 'sequelize-typescript'
@Table
class Person extends Model {
@Column
name: string
@Column
birthday: Date
}
Problemet er at hvis vi deretter henter en sak gjennom:
const p = await Person.findOne({ name: "John Doe" })
console.log(p)
vi får:
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,
}
merk de to siste radene med undefined.
Oppdatering - bruker declare
Det ser ut til at de riktige definisjonene bør bruke declare-nøkkelordet. Selv om dette burde løse problemet, fungerer det ikke i praksis.
import { Table, Column, Model } from 'sequelize-typescript'
@Table
class Person extends Model {
@Column
declare name: string
@Column
declare birthday: Date
}
med konfigurasjonen:
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"],
};
Dessverre er problemet ikke løst, men det ser ut til at declare er mer i tråd med den tiltenkte bruken.
† dette er mest sannsynlig uavhengig av sequelize-typescript-tilleggene
Svar #1
Oppdatering 2022-01-21
Du bør bruke declare i stedet for public ....
For eksempel:
class User extends Model<UserAttributes, UserCreationAttributes>
implements UserAttributes {
//public id!: number; // Note that the `null assertion` `!` is required in strict mode.
declare id: number;
//public name!: string;
declare name: string;
//public preferredName!: string | null; // for nullable fields
declare preferredName: string | null;
// timestamps!
//public readonly createdAt!: Date;
declare readonly createdAt: Date;
Kilde :
- https://github.com/sequelize/sequelize/pull/13877/files
- https://github.com/sequelize/sequelize/issues/11675#issuecomment-1003765448
Originalt svar
Fra https://sequelize.org/master/manual/typescript.html, er TypeScript-definisjonen for modellinitiering korrekt, men dokumentasjonen er ikke korrekt!
Når du bruker null-assertion (public id!: number;/Merk at "null-assertion" "!" er nødvendig i strikt modus), gjør dette attributtet til undefined ved instansinitialisering.
Min idé er å tvinge modellinitialiseringen ved å bruke getDataValue() i Model Constructor og unngå å bruke den anbefalte null assertion.
Eksempel
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');
}
}
