Inquiry on the best practices for handling the "this" keyword in TypeScript within a Vue project

I am new to TypeScript and facing a problem. I have a file with the extension .ts where I store some helper functions. These functions are called using .call so that the correct this is referenced. However, in that file, each use of this generates two errors:

'this' implicitly has type 'any' because it does not have a type annotation.

An outer value of 'this' is shadowed by this container.

Snippet from the component file:

<script lang="ts">
import { defineComponent } from "vue";
import { deleteTodo, getId, setEditId, toggleDone } from "./App.helpers";
import EditTodoDialog from "./components/EditTodoDialog.vue";
import TodoForm from "./components/TodoForm.vue";
import TodoTable from "./components/TodoTable.vue";

export default defineComponent({
  components: { EditTodoDialog, TodoForm, TodoTable },
  data() {
    return {
      addTodoInputValue: "" as string,
      editTodoId: "" as string,
      editTodoInputValue: "" as string,
      todoArr: [] as Array,
    };
  },
  methods: {
    addTodo() {
      if (this.addTodoInputValue) {
        const id = getId();
        const newTodo = {
          handleDelete: () => deleteTodo.call(this, id),
          handleSetEditId: () => setEditId.call(this, id),
          handleToggleDone: () => toggleDone.call(this, id),
          id,
          isDone: false,
          task: this.addTodoInputValue,
        };
        this.todoArr.push(newTodo);
        this.addTodoInputValue = "";
      }
    },
    closeDialog() {
      this.editTodoId = "";
    },
    updateTodo() {
      if (this.editTodoInputValue) {
        const targetIndex = this.todoArr.findIndex(
          ({ id }) => id === this.editTodoId
        );
        this.todoArr[targetIndex].task = this.editTodoInputValue;
        this.editTodoId = "";
        this.editTodoInputValue = "";
      }
    },
  },
  name: "App",
});
</script>

Helper functions file:

export function deleteTodo(targetId: string) {
  const targetIndex = this.todoArr.findIndex(
    ({ id }: { id: string }) => id === targetId
  );
  this.todoArr.splice(targetIndex, 1);
}

export function getId() {
  return `${getRandomNumStr()}-${getRandomNumStr()}`;
}

export function getRandomNumStr() {
  return Math.random().toString().slice(2);
}

export function setEditId(id: string) {
  this.editTodoId = id;
}

export function toggleDone(targetId: string) {
  const targetIndex = this.todoArr.findIndex(
    ({ id }: { id: string }) => id === targetId
  );
  const targetTodo = this.todoArr[targetIndex];
  targetTodo.isDone = !targetTodo.isDone;
}

Answer №1

Your helpers class does not declare the functions on any object, so when you invoke deleteTodo, the only this in scope is the current function. This means that this.todoArr does not exist as it is not declared in the function. Typescript leans towards being more object oriented than regular javascript and may not handle passing around functions in the same way.

You could consider implementing something like this:

export class Helpers {
  public function deleteTodo(todoArr: Array<ToDo>, targetId: string): Array<ToDo> {
    const targetIndex = todoArr.findIndex(
      ({ id }: { id: string }) => id === targetId
    );
    todoArr.splice(targetIndex, 1);
    return todoArr;
  }

  // Any additional methods can be added here
}

You would then call it with

this.todoArr = Helpers.deleteTodo(this.todoArr, 'banana')
among other ways.

If you find this method cumbersome and anticipate calling deleteTodo from multiple locations, you might want to reconsider your structure. Creating a separate TodoService for handling deletions, updates, etc., and then calling it from different components serves as a centralized point of responsibility. Another approach could involve having a TodoHandler class with these methods and having types inherit from it to automatically access the methods and collections. While I have experience with Typescript, I am not familiar with Vue, so I cannot comment on the most conventional approach in that context.

While utilizing helper classes for simple functions like generating Ids works well, making changes to your data model is a more significant structural decision that should probably be housed separately.

Answer №2

For those facing a similar scenario and seeking code references, I have managed to organize things effectively. Here is the link to the repository: https://github.com/username/vue-todo-app

In summary, I developed a TodoList class that handles most of the core logic. As pointed out by another user, TypeScript favors an object-oriented programming approach over traditional JavaScript.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Create a d3 map specifically for a selected region based on the provided latitude and longitude coordinates

I am currently working on developing a d3 map inspired by the codepen created by Andy Barefoot: https://codepen.io/nb123456/pen/zLdqvM?editors=0010. My goal is to adjust the initiateZoom() function in a way that setting specific lat/lon coordinates for a b ...

The CoffeeScript closure _this reference becomes elusive when trapped within multiple nested loops

Something interesting to note about CoffeeScript. Summary: { In CoffeeScript, the fat arrow (=>) creates a closure that stores the reference to `this`. Every instance of @ is replaced with the original value of `this`. For example, the following code: ...

Chunk error ECONNREFUSED trigger

Encountered an issue while running through grunt. Getting a proxy error: Econnrefused when trying to run grunt serve. After running --verbose, it seems like the request is being blocked. I suspect it may be due to my organization's network setup, bu ...

Utilizing the current state within a React callback function closure: A guide to maximising efficiency

I'm currently working on a web page that features a dynamic list of form inputs. Users have the ability to add or remove input fields using designated buttons. To manage this functionality, I've created a parent object called <Ingredients /> ...

The Jest worker has run into 4 child process errors, surpassing the maximum retry threshold

I am a newcomer to Vue and Jest testing, and I keep encountering this error when running a specific test. While I understand that this is a common issue, I am struggling to pinpoint the exact cause of the problem. Here is the error message: Test suite fa ...

Tips for setting an identification value within mongodb?

Currently, my focus is on utilizing node.js and mongoose. I am in the process of developing a REST API to showcase my User model: var userSchema = new Schema({ _id: {type:Number}, username: {type:String}, age: {type:Number}, genre:{type: Number,ref:&a ...

Determining if a user is already logged in from a different device using express-session

After a user logs in, I assign the username to their session with the code: req.session.username = "...."; This identifies the session with a username, but now I need to figure out how to detect if this same username is already logged in from another dev ...

Trouble with transmitting jQuery form information to PHP page

Having mostly worked with PHP, I am now exploring new territories, so please bear with me if this sounds too simple. My goal is to set up a basic registration form that triggers a PHP function to store the entered data. However, it seems like the data is ...

Ensure that JavaScript finishes the animation before transitioning to the next step

Currently, I am developing a basic rotator that cycles through three pieces of advice on a website. My goal is to create a smooth fade in and out effect by adding and removing classes. While testing in the JavaScript debugger, everything runs smoothly as i ...

Tips for preventing JavaScript errors when making cross-domain requests using AJAX

Is there a way to prevent or handle JavaScript errors without causing the script to crash? Error message: No data returned $.ajax({ type : 'GET', dataType : 'jsonp', url : '//cvrapi.dk/api?search=dsfsdfsd&country= ...

`Having difficulties importing images with React JS`

Embarking on the journey of web development, I recently delved into the realms of React JS, Tailwind CSS, and Three.js through an intensive crash course. In my quest to enhance a webpage, I encountered a hurdle when trying to import images from workspace ...

a hyperlink not functioning properly after the # symbol

I’ve been attempting to obtain the URL in order to share it on Google Plus. I’ve experimented with different codes, but the issue is that the ID value is concealed within the URL, making it impossible to directly pass the link in the "a href" tag. The ...

What is the best way to incorporate a button that, when clicked, reveals two separate images on the frontend?

describe food option heredescribe juice option hereHow can I create a button using HTML, CSS, JavaScript, and Bootstrap that displays different images for food and juices when clicked? For example, clicking on "food" will display 3 different food images, w ...

Simply close by clicking outside using basic vanilla JavaScript

I have successfully implemented a menu that closes (removes the added class) when clicking outside the menu button area. Although it is working fine, I am unsure if my code is correct. My understanding is that the click outside functionality should only b ...

Retrieving multiple images from a directory and storing the image filenames in a JSON array

Currently, I am attempting to locate and retrieve the images stored within a specific folder using the following code. This code successfully retrieves the image names along with the total count of images. Subsequently, my goal is to save this information ...

Adjust the background shade of a div according to the color attribute retrieved from a JSON data source

Looking at this snippet, I'm tasked with changing the color of the "header" div to match the color provided in the JSON data. The code snippet is as follows: $("#dropdown").change(function() { $("#header").css("background-color", $(this).val()); }).c ...

Leveraging PHP in conjunction with a database within a JavaScript environment

In this scenario, the code currently hardcodes the sentence "Have a nice day!", causing it to echo out the exact same line. The query being pondered is how one could retrieve a sentence from a database instead of statically coding it. <?php $php_va ...

How can I define a default value for a dynamic route or page in Nuxt.js?

Whenever a user accesses my site through a referral link, I aim for the URL to appear as follows: localhost:3000/<dynamic affiliate username>/home However, in cases where someone visits my site directly as a guest, I would prefer the URL to default ...

How can I make a dropdown menu appear when I select a checkbox?

Is it possible to have a dropdown menu with changing options appear when I click on a checkbox using HTML and JavaScript? I have experimented with some code snippets in JavaScript as a beginner, but I am unsure if they are needed here. Is there an altern ...

Angular: Where does the problem lie - with the application or the deployment?

Currently, I am in the process of creating my own personal website using Angular(v4+). I understand that Angular may seem like a complex framework for a simple website, but I am using it because I am eager to improve my skills with it. During development, ...