Skip to content

Typing errors when using nested creates with associations #1743

Open
@hdeleon99

Description

@hdeleon99

Issue

Following methods do not accept an object with an association,

  • Model.create()

Versions

  • "sequelize": "^6.37.6"
  • "sequelize-typescript": "^2.1.6"

My tsconfig has no strict mode settings enabled. Interestingly, this problem doesn't occur when strictNullChecks is enabled. My tsconfig:

{
  "compilerOptions": {
    "outDir": "./dist",
    "allowJs": true,
    "module": "commonjs",
    "target": "ES2020",
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  },
  "include": ["./src/**/*"]
}

Explanation

I am using sequelize and typescript in a project. I am receiving an error when trying to create a new instance of a model while including one of its hasOne associations. Relevant documentation on the matter: https://sequelize.org/docs/v6/advanced-association-concepts/creating-with-associations/.

// Model A
  export class ModelA extends Model<
    InferAttributes<ModelA>,
    InferCreationAttributes<ModelA>
  > {
  // ... Other fields/columns, skipping for brevity 

  @HasOne(() => ModelB, { onDelete: "SET NULL" })
  modelB: ModelB; 
  }

// Model B
  export class ModelB extends Model<
    InferAttributes<ModelB>,
    InferCreationAttributes<ModelB>
  > {
    @BelongsTo(() => ModelA)
    modelA?: ModelA;
    }

And when I try to create an instance of ModelA, including ModelB:

await ModelA.create({...propertiesOfModelA, 
          modelB: {...propertiesOfModelB}},
          {include: [ModelB],})

I am met with this error:

Type '<fields of modelB argument>' is missing the following properties from type 'ModelB': $add, $set, and 35 more.ts(2740)

ModelA(90, 3): The expected type comes from property 'modelB' which is declared here on type 'Optional<InferCreationAttributes<ModelA, { omit: never; }>, NullishPropertiesOf<InferCreationAttributes<ModelA, { omit: never; }>>>'

I do not expect typescript to enforce that the modelB field in the values argument contain every single field, property, and method of an instance of ModelB. I can remove the stricter typing like this:

export class ModelA extends Model {}

But then Typescript has no way to enforce the types inside of the values argument passed into the create method.

I could typecast, but this feels like a bandaid:

     await ModelA.create({...propertiesOfModelA, modelB: {...propertiesOfModelB} as ModelB,
     {include: [ModelB],})

I could also define the type of ModelA's modelB field as partial, but this incorrectly communicates that all fields and columns in ModelBare optional (even if some are specified as required/not nullable).

    @HasOne(() => ModelB, {onDelete: "SET NULL"})
    modelB: Partial<ModelB>

Even if I were to define ModelB as such, the error persists:

export class ModelB extends Model<Partial<ModelB>> {}

My goal is to maintain type strictness in all relevant model methods (specifically create), so that I can still create models with associations and ensure I'm passing in the correct types.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions