Search code examples
javascriptnode.jsconstructortypeerrormodule.exports

Defined function raises TypeError when tried to be accessed after module.exports


So I followed a udemy course on JS and during the making of an app he writes the code that is written bellow. When I come to run the code an error is raised saying "TypeError: this.validate is not a function". I tried different ways of exporting User and sometimes it told me that it cannot read User as a constructor which is what I want it to be. I have been on this for the past 4 hours and I am still unable to figure out how it works. The whole file is required by other files. When on these other files I create an instance of the object like below. It works although the .push method of an array cannot be accessed(error message pops up)when I call the pushError function

const User = require('../models/User.js')

let user = new User(req.body);
//I can then run the .validate function
user.validate();
//But in that function another error raises that says that the 
//"push cannot be accessed in undefined" 
//And it leads me to think that during the construction the 
//empty list becomes undefined????

let User = function(data) {{
    this.username = data.username;
    this.mail = data.email;
    this.password = data.password;
    this.errors = [];
}
}

User.prototype.validate = function(){
    if(this.username.replace(" ","") == ""){pushError("Username")}
    if(this.password == ""){pushError("Password")}
    if(this.mail.replace(" ","") == ""){pushError("Email")}
}

User.prototype.register = ()=>{
    //Step #1: Validate user Data
    this.validate();
    //Step #2:If validated store data to DB
}

function pushError(str){
    
    this.errors.push(`You must provide a valid ${str}.`);
};

module.exports = User;

If you read through all this thank you!


Solution

  • The problem is that your pushError function is in no way related to the User instance you are creating.

    Inside pushError, this is not the new User object you're attempting to create, hence this.errors is undefined, and you cannot call push on undefined.

    Also, writing register as an arrow function instead of a regular function makes it lose the value of this (this becomes that of the enclosing context, window in a browser or global in Node.js).

    There three steps involved to solve this.

    First you should rewrite pushError as part of User's prototype chain, like so:

    User.prototype.pushError = function(str) {
        this.errors.push(`You must provide a valid ${str}.`);
    };
    

    Second, you should use this.pushError instead of pushError in validate:

    User.prototype.validate = function() {
        if (this.username.replace(" ", "") == "") {
            this.pushError("Username");
        }
        if (this.password == "") {
            this.pushError("Password");
        }
        if (this.mail.replace(" ","") == "") {
            this.pushError("Email");
        }
    }
    

    Third, write register as a regular function:

    User.prototype.register = function() {
        //Step #1: Validate user Data
        this.validate();
        //Step #2:If validated store data to DB
    }
    

    That should do it. Now, a few additional comments and resources. It might help you to:

    • Dig into JavaScript Objects on MDN, especially the Object prototypes section.
    • Write your code as an ES6 class, which is a more "modern" way to do the same thing: this article gives examples of how to write things the "prototype way" or with classes.
    • Learn more about the differences between regular and "fat arrow" functions in this article.