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.
Question: How should I define my babel configuration so that the properties are properly ignored?
Babel definitions (babel.config.js)
I've tried two Babel definitions, one that has all desired features & one that requires explicit definitions.
Full definition
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"],
};
Tiny explicit definition
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"],
};
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)
we get:
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,
}
note the last two rows with undefined.
Update - using 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
}
with the config:
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"],
};
Unfortunately, the issue is not resolved but seems that declare is more in line with the intended use.
† this is most likely independent of the sequelize-typescript add-ons