-
-
Notifications
You must be signed in to change notification settings - Fork 3.8k
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: Typed model lacking type inference within the definitions of static and instance methods #10358
Comments
Most of the issues can be resolved by using That just leaves the |
I can confirm the below script compiles successfully with 928ca4a. The issue is that you'll need to add a 4th generic param to the import { Schema, Model, createConnection } from 'mongoose'
interface ITestModel {
name: string
}
interface InstanceMethods {
iMethod1: (param1: string) => string
iMethod2: (param1: string) => string
}
interface TestModel extends Model<ITestModel, {}, InstanceMethods> {
sMethod1: (param1: string) => string
sMethod2: (param1: string) => string
}
const ModelSchema = new Schema<ITestModel, TestModel, undefined, InstanceMethods>({ // <-- add `InstanceMethods` here
name: String
})
ModelSchema.statics.sMethod1 = function() {
this.sMethod2('test')
}
ModelSchema.statics.sMethod2 = function() {}
ModelSchema.methods.iMethod1 = function() {
this.iMethod2("test")
}
ModelSchema.methods.iMethod2 = function() {
}
// create lazy connection object to connect later
const connection = createConnection()
export const TestModelCompiled = connection.model<ITestModel, TestModel>("testModel", ModelSchema)
const modelInstance = new TestModelCompiled()
modelInstance.iMethod1('test')
TestModelCompiled.sMethod1('test') |
When using mongoose w/ typescript this comment explains how to create Schema and Model with instance methods: Automattic#10358 (comment) ``` import { Schema, Model, createConnection } from 'mongoose' interface ITestModel { name: string } interface InstanceMethods { iMethod1: (param1: string) => string iMethod2: (param1: string) => string } interface TestModel extends Model<ITestModel, {}, InstanceMethods> { sMethod1: (param1: string) => string sMethod2: (param1: string) => string } const ModelSchema = new Schema<ITestModel, TestModel, undefined, InstanceMethods>({ // <-- add `InstanceMethods` here name: String }) ModelSchema.statics.sMethod1 = function() { this.sMethod2('test') } ModelSchema.statics.sMethod2 = function() {} ModelSchema.methods.iMethod1 = function() { this.iMethod2("test") } ModelSchema.methods.iMethod2 = function() { } // create lazy connection object to connect later const connection = createConnection() export const TestModelCompiled = connection.model<ITestModel, TestModel>("testModel", ModelSchema) const modelInstance = new TestModelCompiled() modelInstance.iMethod1('test') TestModelCompiled.sMethod1('test') ``` However due to the change made in this commit: Automattic@fefebb3 The above mentioned code won't compile. The error occurs on this line: `export const TestModelCompiled = connection.model<ITestModel, TestModel>("testModel", ModelSchema)` Output in VSCode (v1.62.2): ``` No overload matches this call. Overload 1 of 3, '(name: string, schema?: Schema<ITestModel, TestModel, {}, {}>, collection?: string, skipInit?: boolean): TestModel', gave the following error. Argument of type 'Schema<ITestModel, TestModel, undefined, InstanceMethods>' is not assignable to parameter of type 'Schema<ITestModel, TestModel, {}, {}>'. Type '{}' is missing the following properties from type 'InstanceMethods': iMethod1, iMethod2 Overload 2 of 3, '(name: string, schema?: Schema<any, Model<any, any, any>, undefined, {}>, collection?: string, skipInit?: boolean): TestModel', gave the following error. Argument of type 'Schema<ITestModel, TestModel, undefined, InstanceMethods>' is not assignable to parameter of type 'Schema<any, Model<any, any, any>, undefined, {}>'. Type '{}' is not assignable to type 'InstanceMethods'.ts(2769) const ModelSchema: Schema<ITestModel, TestModel, undefined, InstanceMethods> ```
@vkarpov15 Using this pattern, how can I refer to document properties within an instance method? For example, the following yields a TypeScript error: interface ITestModel {
name: string
}
interface InstanceMethods {
iMethod1: () => string
}
interface TestModel extends Model<ITestModel, {}, InstanceMethods> {
}
const ModelSchema = new Schema<ITestModel, TestModel, InstanceMethods>({
name: String
})
ModelSchema.methods.iMethod1 = function() {
return this.name // <-- yields "TS2339: Property 'name' does not exist on type '{ iMethod1: (param1: string) => string; }'."
} |
Searching few tutorials, to help TS (or exactly is the Code Editor) find and suggest the instance methods, the Document interface should declare the method. Here are what I wanna say: In the model file
In the controller file
The questions here this is the correct and effective ways to do ? Need you guys to suggest and share your thoughts |
@hungdao-testing it looks like the code you pasted gets autocompleted fine in VSCode with Mongoose 6.2.x: |
@shahar1 the code you pasted compiles fine with Mongoose 6.2, here's a TypeScript playground: https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgWQgEwKYBsA0cDKAxgBYYgCGcAvnAGZQQhwDkIEAdgOYQQDOGzANwAoYcHYwMUWuUIY4ASQAqGXjFSYsiYXF1x25EBgBccNVHGdhVMRKky5i9mvLs5yDDGLpe2vXGAPL3QARlMACgBKOABeAD4zGAsua1tJaVl5FTUNbDgMAA9JdjRfXKwAHmVVdXRsPAQqPAVnGFd3T29ShIRRG0IONRQ6rCJSClj9DAB3AhIycirs2s08ZfLm1vaMIK7eOPDe-wMjU3wky2tI0WFysYWAOiNg0ofAztDJ2gBXNxhgDhRPx6KCeb5QdhwLzAXgPE7yAD0CLgAHVoABrXipIA In the future, please open a new issue and follow the issue template. |
Do you want to request a feature or report a bug?
bug?
What is the current behavior?
When constructing a model that uses Typescript and contains type definitions for static and instance methods, the types of those methods are not "inferred" when implementing the methods later on. A code example is below that might make things clearer.
If the current behavior is a bug, please provide the steps to reproduce.
See this script:
What I was hoping for by defining the types for these models is that the implementations for each instance and static method would infer their types from the definitions provided to the schema. I would also expect that the
this
context in both the instance and static methods would have knowledge of the other instance / static methods available on the model.By this example:
schema.static('methodName, function() {})
style syntax. This allows me to implement the methods in a way that violates the type definitionsthis.sMethod2()
with no arguments I (correctly) get a type error. This is not the case in instance methods, wherethis.iMethod2()
tells me the method is not available.Both instance and static methods have correct types when using them outside the model's methods themselves, as seen on the last two lines.
This may also be due to a misunderstanding on my part of how to define the model's types. I'm relatively new to Typescript so forgive me if I'm getting something wrong.
tsconfig.json
What is the expected behavior?
See above, I was expecting the instance and static methods to correctly infer their types from the model's type definition, and for the instance method context to contain the other instance methods defined on the type.
What are the versions of Node.js, Mongoose and MongoDB you are using? Note that "latest" is not a version.
mongoose: 5.12.13
Node.js: 14.15.0
Typescript: 4.2.3
The text was updated successfully, but these errors were encountered: