Is there a simpler way to create type safe javascript classes that initialize sequelize models?

huangapple go评论73阅读模式
英文:

Is there a simpler way to create type safe javascript classes that initialize sequelize models?

问题

我正在创建一个包含多个数据模型的JavaScript类,我正在使用Sequelize进行管理。我面临的挑战是:

  1. 构造函数方法不能是异步的(截止到2023年4月3日)。
  2. Sequelize的synccreate函数是异步的(因为它们访问数据库,可能是远程数据库)。

然而,我想要将一些基本属性传递给构造函数。所以我的解决方法如下:

import type { Config } from '$lib/server/config.js';
import { Model, Sequelize, DataTypes } from 'sequelize';
import type { ModelDefined, InferAttributes, InferCreationAttributes, CreationOptional } from 'sequelize';

class CourseDescription extends Model<InferAttributes<CourseDescription>, InferCreationAttributes<CourseDescription>> {
    declare id: CreationOptional<number>;
    declare name: string;
}

export class Course {
    protected sequelize: Sequelize;
    protected Description: ModelDefined<InferAttributes<CourseDescription>, InferCreationAttributes<CourseDescription>>;
    public initComplete: Promise<boolean>;

    constructor(protected config: Config, name: string) {
        this.sequelize = new Sequelize(this.config.postgres_url);
        
        const desc = this.sequelize.define("CourseDescription", 
            {
                id: {
                    type: DataTypes.INTEGER,
                    autoIncrement: true,
                    primaryKey: true
                },
                name: DataTypes.TEXT
            }
        )

        // 为了避免TypeScript的“used before assigned”错误而采用的解决方法
        this.Description = desc;

        this.initComplete = (async () => {         
            await desc.sync();
            await desc.create({ name });
            return true;
        })()
    }
}

然后当我使用这个类时:

import config from './lib/server/config';
import { Course } from './lib/server/course';

const m = new Course(config, "My Excellent Course");
await m.initComplete;

但对我来说,这似乎是一个相对简单任务的冗长方法。(而且为了避免TypeScript的“property used before assigned”错误而采用的解决方法相当丑陋。)

是否有更简洁的方法来定义Sequelize模型并通过类构造函数创建记录?

英文:

I am creating a javascript class that contains several data models, which I am managing with sequelize. The challenge I am facing is that (1) the constructor method cannot be async (as of 4/3/2023), but (2) the sequelize sync and create functions are async (as well they should be as they are accessing a database, and a possibly remote database at that). However, I would like some basic attributes to be passed to the constructor. So my work around is like this:

import type { Config } from &#39;$lib/server/config.js&#39;;
import { Model, Sequelize, DataTypes } from &#39;sequelize&#39;;
import type { ModelDefined, InferAttributes, InferCreationAttributes, CreationOptional } from &#39;sequelize&#39;;

class CourseDescription extends Model&lt;InferAttributes&lt;CourseDescription&gt;, InferCreationAttributes&lt;CourseDescription&gt;&gt; {
    declare id: CreationOptional&lt;number&gt;;
    declare name: string;
}

export class Course {
    protected sequelize:Sequelize;
    protected Description:ModelDefined&lt;InferAttributes&lt;CourseDescription&gt;, InferCreationAttributes&lt;CourseDescription&gt;&gt;
    public initComplete:Promise&lt;boolean&gt;;

    constructor(protected config:Config, name: string) {
        this.sequelize = new Sequelize(this.config.postgres_url);
        
        const desc = this.sequelize.define(&quot;CourseDescription&quot;, 
            {
                id: {
                    type: DataTypes.INTEGER,
                    autoIncrement: true,
                    primaryKey: true
                },
                name: DataTypes.TEXT
            }
        )

        // work around to avoid &quot;used before assigned&quot; error below
        this.Description = desc;

        this.initComplete = (async () =&gt; {         
            await desc.sync();
            await desc.create({ name});
            return(true);
        })()
    }
}


Then when I use the class:

import config from &#39;./lib/server/config&#39;;
import { Course } from &#39;./lib/server/course&#39;

const m = new Course(config, &quot;My Excellent Course&quot;);
await m.initComplete;

But that seems to me to be an awfully long walk to achieve a relatively simple task. (And also the work around to avoid the "property used before assigned" error from TypeScript is pretty ugly.)

Is there a more parsimonious approach to defining a sequelize model and creating a record via the class constructor?

答案1

得分: 1

在不考虑 sequelize 的情况下,一个常见的模式是使用一个私有构造函数和工厂方法来创建依赖于某些异步行为的实例:

class ClassRequiringAsyncInitialization {
    private constructor(private someProperty: string) {}

    static async create() {
        const someProperty = await new Promise<string>(
            (resolve) => setTimeout(() => resolve('property'))
        );

        return new ClassRequiringAsyncInitialization(someProperty);
    }
}

const x = ClassRequiringAsyncInitialization.create(); // 可以等待
const y = new ClassRequiringAsyncInitialization('property'); // 错误
英文:

Taking sequelize out of the equation, a common pattern is to use a private constructor with a factory method to create instances that rely on some asynchronous behavior:

class ClassRequiringAsyncInitialization {
    private constructor(private someProperty: string) {}

    static async create() {
        const someProperty = await new Promise&lt;string&gt;(
            (resolve) =&gt; setTimeout(() =&gt; resolve(&#39;property&#39;))
        );

        return new ClassRequiringAsyncInitialization(someProperty);
    }
}

const x = ClassRequiringAsyncInitialization.create(); // OK, can be awaited
const y = new ClassRequiringAsyncInitialization(&#39;property&#39;); // error

huangapple
  • 本文由 发表于 2023年4月4日 09:58:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/75924956.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定