Home > Software design >  TypeError: Cannot read properties of undefined (reading 'location') in res.redirect
TypeError: Cannot read properties of undefined (reading 'location') in res.redirect

Time:04-13

I have the following function to retrieve an object from a database and extract an URL:

async redirect(id: string, redirectFunction: Function) {
    if (!IdExists(id)) {
      throw new Error(`ID ${id} does not exist.`);
    }
    const redirectLocation: string = await prisma.url.findUnique({
        where: { uniqueId: id },
        select: { url: true },
    }).then((data) => {
        return data?.url!;
    });

    redirectFunction('http://'   redirectLocation); 
}

The function is called in the following segment of code:

app.get('/:id', async (req, res) => {
    try {
      redirectController.redirect(req.params.id, res.redirect);
    } catch (error) {
      console.error(error);
    } 
});

However, I get the TypeError: Cannot read properties of undefined (reading 'location'), I see that the error is related to the res.redirect method. However, when I replace it by console.log for debugging, the URL is showed properly. What may be causing this error?

CodePudding user response:

This line of code:

redirectController.redirect(req.params.id, res.redirect);

Passes res.redirect (a function reference) as the second argument, but all that is passed is just the function so the res gets lost when you later try to call it. That causes the method to have a wrong this value when it executes and lots of things go wrong.

You can fix that several different ways. Once such way is with .bind():

redirectController.redirect(req.params.id, res.redirect.bind(res));

.bind() creates a small stub function that remembers the value of res so that when the stub function is called, it will be called with the right res reference and thus the this value inside the function will be correct.

Another way to solve it is to create your own little stub function:

redirectController.redirect(req.params.id, (...args) => {
   res.redirect(...args);
});

When it calls your stub function, you call res.redirect() properly and pass it whatever arguments the controller called your stub function with.


As a small demonstration, you can see this effect here:

const obj = {
    greeting: "Hello",
    talk: function() {
        if (this && this.greeting) {
            console.log(`this.greeting is "${this.greeting}"`);
        } else {
            console.log("value of this is wrong");
        }
    }
}

console.log("calling as obj.talk()");
obj.talk();                             // works

console.log("-------------------------");

// function we pass a method to and then call that method
function callTalk(fn) {
     fn();
}

console.log("calling by passing method to another function");
callTalk(obj.talk);                     // doesn't work

// call it using .bind()
console.log("-------------------------");
console.log("calling using .bind()");
callTalk(obj.talk.bind(obj));           // works

  • Related