JS: How to interpret what is the return type in VS Code?

Eg. I have this pop-up in VSCode when I hover over the model method in
const Test = mongoose.model(‘test’, tourSchema);

function model(name: string, schema?: Schema, collection?: string, skipInit?: boolean): Model<Document, {}> (+1 overload)

  1. What does model mean? Return a Document object?
  2. What does :Model<Document, {}> mean? Return a Document object? If so, how is it different from (1)?
  3. What does Schema mean? Return a Schema object? Return a Schema constructor?
  4. I guess name argument is mandatory and the rest of the arguments are optional, correct?

In short, what are in between the angle brackets in Javascript?

Thank you in advance!

It’s not JavaScript, it’s TypeScript. VSCode’s JS tooling is also TypeScript tooling, the two are the same. This doesn’t mean you’re writing TypeScript, it just means that you can see the TypeScript definitions if they’re available, they are there to help you.

  • This is a function called model that returns a type called Model.
  • It has a name parameter that is mandatory…
  • …and the parameters schema, collection and skipInit which are optional (the ? after them denotes this)
  • Schema is a specific, defined type (that is why it’s capitalised). You should be able to right-click on the argument and select “go to definition” to show the code where it is defined, or “go to type definition” to view the type. In this case, Schema is a class
  • string and boolean are built in types, they are what they say on the tin.
  • The benefit of this is that if you typed const Test = mongoose.model(1) it would show an error (red squiggly line under the 1) straightaway: the first parameter of the model function has to be a string. If you were using TypeScript rather than JavaScript, this would also cause the compilation from TS -> JS to fail with an error stating you were trying to pass a number instead of a string to the function.
  • Model is an instance of this
  • The angle brackets denote that this is what’s called a generic. It means that it can work over a number of different underlying types. In this case, a model creates a Document type which may include the Schema. The Schema may be one of a variety of different types, so the second parameter in those angle brackets is saying it’s going to work on an object.

I need to emphasise that you don’t need to know all this, the hints here are there to describe the shape of the API you’re working with (Mongoose), and are included from the Mongoose code, but you can just ignore them if you want.

Thank you very much for your detailed and systematic explanation. Much appreciated. It gives me the much needed guidance to further my studies.

You mentioned the model function returned an instance of the Model class. However, when I did a typeof on the variable assigned with the returned value of the model function, it states that a function is returned instead of object. (#1)

const employeeModel = mongoose.model(‘employee’, employeeScheme);
console.log(typeof employeeModel); // #1 print ‘function’ in console

Also, I noticed that I can use the ‘new’ keyword on employeeModel like:

const employee = new employeeModel( {
// some other codes here
} );

Why is the new keyword allowed to be used with/on the employeeModel, which is an instance /object?

Thank you!

I didn’t say it was an object. It’s a constructor https://mongoosejs.com/docs/models.html, which builds an object.

It’s a constructor

Referring to the code snippet below taken from the models doc at mongoosejs website, am I right to say that create() and insertMany() are properties of the constructor function that was returned by mongoose.model() and assigned to Tank?

Thanks.

var Tank = mongoose.model('Tank', yourSchema);

Tank.create({ size: 'small' }, function (err, small) {
if (err) return handleError(err);
  // saved!
});

Tank.insertMany([{ size: 'small' }], function(err) {
});

No, the function doesn’t have properties. A constructor creates an object with those functions attached to it.

The point of the model function (the class constructor) is that it builds an object populated with data you pass it relating to the DB Schema, and it attaches some predefined functions to it that now operate on that specific data.

This is what that confused me. There is no code (like new Tank()) that instantiate the object before calling the create/insertMany method using the dot notation.

Since Tank is a constructor function, Tank.create() seems to have the syntax of a constructor function calling the create method. That’s why I thought create() could be a property of the constructor function.

Tank.create()
<constructor function>.<method> // ??
vs
(new Tank()).create()
<object>.<method> // the usual way of calling a method?

Thank you for your patience on this topic! Much appreciated!

It will work like this. Note I’m on phone and I don’t want to spelunk through the Mongoose codebase to show exactly how they’ve set it up, it will be doing a helluva lot more stuff, this is drastically simplified and doesn’t actually do anything:

class Model {
  constructor (name, schema) {
    this.name = name;
    this.schema = schema;
  }

  static create(name, schema) {
    return new Model(name, schema);
  }
}

Now this allows you to either do

new Model("MyName", mySchema)

Or

Model.create("MyName", mySchema)

They both do the same thing

Without class syntax:

function Model (name, schema) {
  this.name = name;
  this.schema = schema;
}

Model.create = function(name, schema) {
  return new Model(name, schema);
}

Thank you very much! This clears up everything.

Is there a way to know from the API doc what is the exact return type of a given method?
Eg.

Model.find()
Parameters

    filter «Object|ObjectId»
    [projection] «Object|String» optional fields to return, see Query.prototype.select()

    [options] «Object» optional see Query.prototype.setOptions()

    [callback] «Function» 

Returns:

    «Query» 

The doc indicates returning a Query object but it does not indicate that it is actually an Array of Documents. I used console.log(myModel.find()) to know that. Likewise for other methods, I have to console.log for the actual returns. Is there a better way to know?

Thank you!