skip to Main Content

I am building a Node application using TypeScript and MongoDB as my application’s database. I am using Typegoose to interact with MongoDB from Node JS application. Now, I am having a problem with using Nested Discriminator, https://typegoose.github.io/typegoose/docs/guides/advanced/nested-discriminators/ in my application.

I have a model called Workflow with the following code.

export class Workflow {
  @prop({ required: true, type: Date, default: new Date() })
  public createdAt!: Date;

  @prop({ required: true, type: () => [WorkflowTask] })
  public tasks!: WorkflowTask[];
}

export const WorkflowModel = getModelForClass(Workflow);

As you can see, a workflow can have many tasks (WorkflowTask). Below is the dummy code of my WorkflowTask model.

export class WorkflowTask {
  @prop({ required: false, type: Schema.Types.String })
  public title?: string;

  @prop({ required: false, type: Schema.Types.String })
  public description?: string;

  @prop({ required: true, type: Schema.Types.String })
  public type!: WorkflowTaskType;

  @prop({
    required: true,
    type: FormContent,
    discriminators: () => [
      {
        type: TextFormFieldContent,
        value: FormWorkflowTaskContentType.TEXT_FORM_FIELD
      },
      {
        type: NumberFormFieldContent,
        value: FormWorkflowTaskContentType.NUMBER_FORM_FIELD
      }
    ],
    default: []
  })
  public formContents!: FormContent[];
}

As you can see, a WorkflowTask can have many FormContent where I put the discriminator in.

The following are some dummy code for my Workflow content classes.

export class FormContent {
  @prop({ required: false, type: Schema.Types.String })
  public label?: string;

  @prop({ required: false, type: Schema.Types.String })
  public message?: string;

  @prop({ required: false, type: Schema.Types.String })
  public description?: string;

  @prop({ required: true, type: Schema.Types.String })
  public type!: FormWorkflowTaskContentType;
}

export class TextFormFieldContent extends FormContent {
  @prop({ required: false, type: Schema.Types.String })
  public defaultValue?: string;
}

export class EmailFormFieldContent extends FormContent {
  @prop({ required: false, type: Schema.Types.String })
  public defaultValue?: string;
}

export class NumberFormFieldContent extends FormContent {
  @prop({ required: false, type: Schema.Types.String })
  public defaultValue?: string;
}

export class MultiSelectFieldContent extends FormContent {
  @prop({ required: false, type: Schema.Types.Array, default: [] })
  public defaultValue?: string[];
}

At the moment, I am only trying to create a workflow with empty tasks using the following code.

  await WorkflowModel.create({
    createdAt: new Date(),
    tasks: []
  });

I am getting the following error when I run the code.

ValidationError: Workflow validation failed: tasks: Cast to Embedded failed for value "[]" (type Array) at path "tasks" because of "ObjectExpectedError"

I am not even populating the tasks for the workflow. I followed the page correctly. What is wrong with my code and how can I fix it?

2

Answers


  1. Looking at the typegoose documentation for the type option of the @prop decorator it seems like the type you pass should be the type of the array’s items:

    Example: Arrays (array item types can’t be automatically inferred via Reflect)

    class Dummy {
      @prop({ type: String })
      public hello: string[];
    }
    

    If that’s the case the decorator may be defined as:

      @prop({ required: true, type: () => WorkflowTask })
      public tasks!: WorkflowTask[];
    

    or

      @prop({ required: true, type: WorkflowTask })
      public tasks!: WorkflowTask[];
    
    Login or Signup to reply.
  2. I dont see anything obviously wrong with your provided code, and i also locally tried to test your code which was working just fine.

    Reproduction Repository / Branch: https://github.com/typegoose/typegoose-testing/tree/SO74916118

    But there are some notes:

    • you can improve readability by replacing Schema.Types.String with String, typegoose & mongoose will automatically translate it to the proper Schema-Type
    • type: Schema.Types.Array is likely not what you want to set, this will effectively be of the Mixed type, in this case String would be appropiate
    • default: new Date() is likely not what you want, you probably want to change it to default: () => new Date(), the way it is currently defined evaluates default: new Date() at the time of loading the file once instead of everytime a document is created
    • some values were not provided and have been inferred in the reproduction code (not provided were FormWorkflowTaskContentType and WorkflowTaskType)

    PS: another answer has pointed out that type: () => [WorkflowTask] is not supported, which is not true; the syntax of type: [Type] is supported since typegoose 7.4.0


    Slight Note:
    if you code fails, you may just be on old versions or you are running into Circular Dependencies

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search