Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TypeScript: Property 'discriminator' does not exist on type 'SchemaType'. #10435

Closed
drewkht opened this issue Jul 8, 2021 · 6 comments
Closed
Labels
docs This issue is due to a mistake or omission in the mongoosejs.com documentation typescript Types or Types-test related issue / Pull Request
Milestone

Comments

@drewkht
Copy link

drewkht commented Jul 8, 2021

Do you want to request a feature or report a bug?

bug

What is the current behavior?

When adding a discriminator for a single nested subdocuments (per the example in the blog post here: https://thecodebarbarian.com/mongoose-4.12-single-embedded-discriminators.html), TypeScript doesn't recognize the .discriminator method, reporting that it doesn't exist on type "SchemaType".

image

The code still works properly if the errors are suppressed:

image

If the current behavior is a bug, please provide the steps to reproduce.

import mongoose, { Model } from 'mongoose';

interface EventDetailsSchema {
  message: string;
}

const eventDetailsSchema = new mongoose.Schema<
  EventDetailsSchema,
  Model<EventDetailsSchema>,
  EventDetailsSchema
>(
  { message: { type: String, required: true } },
  { discriminatorKey: 'kind', _id: false }
);

interface ClickedEventSchema extends EventDetailsSchema {
  element: string;
}

const clickedEventSchema = new mongoose.Schema<
  ClickedEventSchema,
  Model<ClickedEventSchema>,
  ClickedEventSchema
>({
  element: { type: String, required: true },
});

interface PurchasedEventSchema extends EventDetailsSchema {
  product: string;
}

const purchasedEventSchema = new mongoose.Schema<
  PurchasedEventSchema,
  Model<PurchasedEventSchema>,
  PurchasedEventSchema
>({
  product: { type: String, required: true },
});

interface EventSchema {
  createdAt: Date;
  props: ClickedEventSchema | PurchasedEventSchema;
}

const eventSchema = new mongoose.Schema<
  EventSchema,
  Model<EventSchema>,
  EventSchema
>({
  createdAt: Date,
  props: eventDetailsSchema,
});

eventSchema.path('props').discriminator('Clicked', clickedEventSchema);
eventSchema.path('props').discriminator('Purchased', purchasedEventSchema);

const Event = mongoose.model('Event', eventSchema);

const e = new Event({
  createdAt: new Date(),
  props: {
    message: 'test',
    kind: 'Clicked',
    element: '#hero-image',
  },
});

console.log(e, e.validateSync());

My tsconfig.json:

{
  "compilerOptions": { 
    "alwaysStrict": true,
    "baseUrl": "src",
    "declaration": true,
    "declarationMap": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "incremental": true,
    "importHelpers": true, 
    "isolatedModules": true,
    "module": "ESNext",
    "moduleResolution": "node",  
    "noEmit": true,  
    "noImplicitAny": true,
    "noImplicitThis": true,
    "resolveJsonModule": true,
    "skipLibCheck": true,
    "sourceMap": true,
    "strict": true,
    "target": "ESNext",
    "allowJs": true,   
    "noFallthroughCasesInSwitch": true,
    "noUncheckedIndexedAccess": true,   
    "lib": [
      "dom",
      "dom.iterable",
      "ESNext"
    ]
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules/**/*"],
  "ts-node": {
    "files": true
  }
}

What is the expected behavior?

TypeScript sees .discriminator method exists when called on instance of SchemaType returned by .path method, when path resolves to a single embedded subdocument

What are the versions of Node.js, Mongoose and MongoDB you are using? Note that "latest" is not a version.

Node: v16.3.0
Mongoose: v5.13.2
MongoDB NPM package: v3.6.8 (from Mongoose dependency)
MongoDB server: v3.4.18
TypeScript: v4.3.5

@IslandRhythms IslandRhythms added the typescript Types or Types-test related issue / Pull Request label Jul 9, 2021
@vkarpov15 vkarpov15 added this to the 5.13.3 milestone Jul 11, 2021
vkarpov15 added a commit that referenced this issue Jul 16, 2021
@vkarpov15
Copy link
Collaborator

There's a couple of Mongoose issues here:

  1. Single nested subdocs don't implements AcceptsDiscriminator. That's fixed in 34d2796 .
  2. Schema#path() always returns SchemaType, so can't use .discriminator(). This was also mentioned in Embedded discriminators with "tiedValue" dont populate without explicitly specifing "model" #10231 (comment) . 2 possible workarounds here: either somehow make Schema#path() type bindings infer the correct type, or make type bindings allow creating an instance of Schema.Types.Embedded and pass that to the schema.

@vkarpov15
Copy link
Collaborator

I tried a few workarounds, including using a Schema.Types.Embedded constructor, but nothing seems to work. In particular, the Schema.Types.Embedded constructor gets very messy because of schema constructor generics.

Here's the workaround that we'll support as of v5.13.3:

eventSchema.path<Schema.Types.Embedded>('props').discriminator('Clicked', clickedEventSchema);
eventSchema.path<Schema.Types.Embedded>('props').discriminator('Purchased', purchasedEventSchema);

@AaronPorts
Copy link

Mongoose 7.2.1. Is the best approach now:

eventSchema.path<Schema.Types.Subdocument>('props').discriminator('Clicked', clickedEventSchema);

?

@vkarpov15
Copy link
Collaborator

@AaronPorts yeah that is the recommended workaround. The issue is that Mongoose's SchemaType base class does not have a discriminator() function. Schema.Types.Subdocument does have a discriminator() function, but eventSchema.path() doesn't currently automatically resolve that 'props' is a Subdocument path, just returns SchemaType by default.

@JustYourBud
Copy link

This worked for me also, thanks for the answers. Are there any plans to have path resolve a Subdocument path automatically when needed, or will this be a needed workaround for the foreseeable future? It feels troublesome to have to find this (closed) issue just for a solution to what seems like an unintended bug, as there's no documentation anywhere regarding this quirk.

@vkarpov15
Copy link
Collaborator

This will be a needed workaround for the foreseeable future. I'll add docs.

@vkarpov15 vkarpov15 reopened this Aug 10, 2023
@vkarpov15 vkarpov15 modified the milestones: 5.13.3, 7.4.4 Aug 10, 2023
@vkarpov15 vkarpov15 added the docs This issue is due to a mistake or omission in the mongoosejs.com documentation label Aug 10, 2023
vkarpov15 added a commit that referenced this issue Aug 12, 2023
vkarpov15 added a commit that referenced this issue Aug 13, 2023
docs: add brief note on TypeScript generic usage for embedded discriminator `path()` calls
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs This issue is due to a mistake or omission in the mongoosejs.com documentation typescript Types or Types-test related issue / Pull Request
Projects
None yet
Development

No branches or pull requests

5 participants