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

mongoose.InferSchemaType does not correctly infer nullable arrays #12420

Closed
2 tasks done
ehenon opened this issue Sep 12, 2022 · 12 comments · Fixed by #12649
Closed
2 tasks done

mongoose.InferSchemaType does not correctly infer nullable arrays #12420

ehenon opened this issue Sep 12, 2022 · 12 comments · Fixed by #12649
Labels
typescript Types or Types-test related issue / Pull Request
Milestone

Comments

@ehenon
Copy link

ehenon commented Sep 12, 2022

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the bug has not already been reported

Mongoose version

6.5.5

Node.js version

16.15.1

MongoDB server version

5.0.12

Description

When working with Mongoose schemas involving array fields that are nullable, mongoose.InferSchemaType does not seem to correctly infer the schema type. The notion of optionality does not seem to be reflected.

Steps to Reproduce

Let's suppose we create a Mongoose schema with 2 fields of 2 different types, one being an array of strings, the other an array of objects, both optional.

const TestSchema = new mongoose.Schema(
  {
    comments: { type: [String], default: () => undefined, required: false },
    reference: {
      type: [
        {
          type: { type: String, required: false },
          value: { type: String, required: false },
        },
      ],
      default: () => undefined,
      required: false,
    },
  },
  {
    collection: 'tests',
    timestamps: false,
  },
);

export type TestType = mongoose.InferSchemaType<typeof TestSchema>;

In TestType, the notion of optionality of our fields seems to be lost:
inferred_type

The problem is the same if we replace default: () => undefined by default: () => null.

If we just try default: undefined or default: null, another problem appears:
inferred_type_2

Perhaps there is a misunderstanding/error on my side in defining the schema. If this is the case, I would be happy to receive your advices. Otherwise, it must be a bug? 🐛

Expected Behavior

{
    comments?: string[];
    reference?: {
        type?: string;
        value?: string;
    }[];
}
@Uzlopak
Copy link
Collaborator

Uzlopak commented Sep 12, 2022

@mohammad0-0ahmad

@IslandRhythms IslandRhythms added the typescript Types or Types-test related issue / Pull Request label Sep 12, 2022
lpizzinidev added a commit to lpizzinidev/mongoose that referenced this issue Sep 17, 2022
@vkarpov15
Copy link
Collaborator

The issue here has to do with default, not required. Mongoose treats any field that has a default as non-nullable, because if the field's value is nullish Mongoose will apply the default.

In this case, that is a problem because the default is always undefined. We should explicitly check for default: undefined.

@vkarpov15 vkarpov15 modified the milestones: 6.6.3, 6.6.4 Sep 26, 2022
@vkarpov15 vkarpov15 modified the milestones: 6.6.4, 6.6.5, 6.6.6 Oct 3, 2022
@vkarpov15 vkarpov15 modified the milestones: 6.6.6, 6.6.7, 6.6.8 Oct 19, 2022
@hoangbn
Copy link

hoangbn commented Oct 29, 2022

It appears to also be the issue with required as

const TestSchema = new Schema(
  {
    comments: { type: [String], required: false }
  }
);

export type TestType = InferSchemaType<typeof TestSchema>;

gives

type TestType = {
    comments: string[];
}

instead of

type TestType = {
    comments?: string[];
}

@hoangbn
Copy link

hoangbn commented Oct 29, 2022

The same is true for nested schemas. They aren't optional, even though required: true is not used

vkarpov15 added a commit that referenced this issue Nov 2, 2022
vkarpov15 added a commit that referenced this issue Nov 2, 2022
fix(types): make array paths optional in inferred type of array default returns undefined
@Jokero
Copy link
Contributor

Jokero commented Nov 4, 2022

@vkarpov15 I have the same issue as @hoangbn with array type (6.7.1 version):

import { Schema, InferSchemaType } from 'mongoose';

const sightSchema = new Schema({
    name: { type: String, required: true },
    location: { type: [Number], required: false } // all ways to define "location" give the same result
    // location: { type: [Number] }
    // location: [Number]
});

type Sight = InferSchemaType<typeof sightSchema>;

// Property 'location' is missing in type '{ name: string; }' but required in type '{ name: string; location: number[]; }'
const sight: Sight = {
    name: 'Red Square',
};

@Jokero
Copy link
Contributor

Jokero commented Nov 5, 2022

Oh, I spent a lot of time to figure out that in order for default: undefined/default: () => undefined to work I need to have strict: true or strictNullChecks: true in tsconfig.json. No such information in documentation

@vkarpov15 vkarpov15 reopened this Nov 18, 2022
@vkarpov15 vkarpov15 modified the milestones: 6.7.1, 6.7.5 Nov 18, 2022
@vkarpov15 vkarpov15 modified the milestones: 6.8.1, 6.8.2 Dec 19, 2022
@vkarpov15
Copy link
Collaborator

vkarpov15 commented Dec 22, 2022

Confirmed that this issue still happens if strict: true isn't set. Following script fails to compile without --strict. We're looking into that to see if we can fix it.

import mongoose from 'mongoose';

const TestSchema = new mongoose.Schema(
  {
    comments: { type: [String], default: () => undefined, required: false },
    reference: {
      type: [
        {
          type: { type: String, required: false },
          value: { type: String, required: false },
        },
      ],
      default: () => undefined,
      required: false,
    },
  },
  {
    collection: 'tests',
    timestamps: false,
  },
);

export type TestType = mongoose.InferSchemaType<typeof TestSchema>;
const doc: TestType = { comments: undefined };

@vkarpov15
Copy link
Collaborator

It looks like InferSchemaType doesn't work very well without strict set. The inferred type is unknown in TypeScript 4.8.4. Are you seeing this too @Jokero ?

$ ./node_modules/.bin/tsc  gh-12420.ts 
gh-12420.ts:13:7 - error TS2741: Property 'location' is missing in type '{ name: string; }' but required in type '{ location: unknown[]; name?: unknown; }'.

13 const sight: Sight = {
         ~~~~~


Found 1 error in gh-12420.ts:13

I think for now we'll just add a note to use strict mode with inferred schema types.

@Jokero
Copy link
Contributor

Jokero commented Dec 22, 2022

@vkarpov15 Yes, I also get { location: unknown[]; name?: unknown; }.

However, it infers unknown only when strict/strictNullChecks is missing and there is default: undefined/default: () => undefined.

Without default, I get { name: string; location: number[]; } no matter if strict/strictNullChecks is present or not.

vkarpov15 added a commit that referenced this issue Dec 27, 2022
docs(typescript): make note about recommending `strict` mode when using auto typed schemas
@vkarpov15
Copy link
Collaborator

Docs fix in #12825

@olawalejuwonm
Copy link

Please this inferschemaType does not infer methods too

@vkarpov15
Copy link
Collaborator

@olawalejuwonm can you please open a new issue and follow the issue template?

@Automattic Automattic locked and limited conversation to collaborators May 1, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
typescript Types or Types-test related issue / Pull Request
Projects
None yet
7 participants