Update the event listeners for child components

During my transition from Vue2/JavaScript to Vue3/TypeScript, I encountered a difficulty with migrating a computed property that remaps component listeners to child components based on a prefix. In Vue2/JavaScript, the computed property looked like this:

inputPortListeners() {
  return reduce(
    this.$listeners,
    (listeners, listener, name) => {
      if (!name.startsWith('input-')) return listeners;
      listeners[name.slice('input-'.length)] = (e) => listener.call(this, e, this.node.id, e.target.dataset.index);
      return listeners;
    },
    {}
  );
},

In Vue3/TypeScript, I attempted to migrate it as follows:

type Listeners = Record<string, (e: unknown)=>void>;
const inputPortListeners = computed(() => {
  return Object.keys(attrs).reduce<Listeners>((listeners, name: string) => {
    if (!name.startsWith('onInput')) return listeners;
    listeners[name.replace('onInput', '')] = (e: unknown) => attrs[name].call(this, e, this.node.id, e.target.dataset.index);
    return listeners;
  }, {});
});

The specific issue lies in this line of code:

(e: unknown) => attrs[name].call(this, e, props.node.id, e.target.dataset.index);

This is problematic because attrs does not have a strong type and e is of type unknown, which prevents access to e.target.dataset.index.

What are some possible solutions for resolving this concern?

Answer №1

attributes (retrieved from the setup()'s context argument) is considered to be a Data, and it is defined as such:

Record<string, unknown>

Hence, when accessing attrs[name], it returns an unknown type, which requires type assertion for certain operations:

(attrs[name] as Function).call(...)

Furthermore, the variable e is also of type unknown, but it can easily be specified. In this case, e represents an Event, so changing from (e: unknown) to (e: Event) makes more sense:

type Listeners = Record<string, (e: Event)=>void>;
//...
listeners[name.replace('Input', '')] = (e: Event) => {...};

The Event interface includes a property called target, which is specified as follows:

EventTarget | null

However, EventTarget does not have the dataset property on its own, which actually belongs to HTMLElement. To access the dataset, you must assert the type to HTMLElement, assuming that the target element is likely an <input> based on the event being triggered:

(e.target as HTMLElement)?.dataset.index

You should refactor your code accordingly:

type Listeners = Record<string, (e: Event)=>void>;
const inputPortListeners = computed(() => {
  return Object.keys(attrs).reduce<Listeners>((listeners, name: string) => {
    if (!name.startsWith('onInput')) return listeners;
    listeners[name.replace('Input', '')] = (e: Event) => (attrs[name] as Function).call(this, e, null, (e.target as HTMLElement)?.dataset.index);
    return listeners;
  }, {});
});

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

Enhancing Javascript-written React components with Typescript typings

Looking for guidance on incorporating TypeScript typings into my existing set of React components written in JavaScript and Flow. Unsure about the best approach, so any assistance would be greatly valued. The current project structure is as follows: / | ...

Unraveling nested elements with the array map() method in Angular2 and Typescript: Fixing the issue of undefined property reference while mapping

Hey there! I'm currently working with Angular 4 and I have a piece of code that parses data from an API into a TypeScript array of rows. It's important to note that the code functions properly if elements like 'item.tceCampRun' and &apo ...

Executing API request from local server for production environment

I recently deployed my application to Heroku using npm run build. However, I encountered an issue where the API calls made in Heroku production are pointing to my localhost. Can anyone provide guidance on how to resolve this? api_axios.js const axios = r ...

Tips for effectively generating a JSON object array in Typescript

Currently, I'm attempting to construct an array of JSON objects using TypeScript. Here is my current method: const queryMutations: any = _.uniq(_.map(mutationData.result, function (mutation: Mutation) { if (mutation && mutation.gene) { co ...

Utilizing Arrays in Typescript within the Angular Framework

I have developed a Rest API that provides data to populate two drop-down lists in a form. The information retrieved from the API is grabbed by the Angular backend and assigned to the respective drop-downs. Rather than making separate Get requests for each ...

Typescript: The property 'userId' is not found within the 'unknown' type

For my rxJS practice in Typescript, I wrote the following simple code. import fetch from 'node-fetch'; import { Observable, Subject, asapScheduler, pipe, of, from, interval, merge, fromEvent, SubscriptionLike, PartialObserver } from &apo ...

What is the best way to use a computed property as a style value for a component in Vue.js?

My component's template includes the following HTML element: .grid-item(:style="{ width: columnWidth, backgroundColor: 'blue' }") I want to dynamically set the width of this element using a computed property: computed: { columnWidth () ...

What is the best way to display loading details during a data loading process within a useEffect hook?

Whenever a specific custom React component I've created is initially mounted, it utilizes useEffect to initiate a lengthy multistep process of loading data that will later be rendered. Since the component isn't always rendered, this costly proces ...

Can you explain how this promise functions within the context of the mutation observer, even without an argument?

Recently, I came across a mutation observer in some TypeScript code that has left me puzzled. This particular implementation of a promise within the mutation observer seems unconventional to me: const observer = new MutationObserver((mutations: MutationR ...

Jest is having trouble recognizing a custom global function during testing, even though it functions properly outside of testing

In my Express app, I have a custom function called foo that is globally scoped. However, when running Jest test scripts, the function is being recognized as undefined, causing any tests that rely on it to fail. This is declared in index.d.ts: declare glob ...

Need help restarting a timer I've built in Angular with just a click?

I am in the process of developing a compact application that will help me keep track of my activities within a specific time frame. Once I input an activity into a field and click "OK," it should be added to a list. My current challenge lies in resetting ...

Express not functioning properly with custom error handler

I'm facing an issue while trying to implement a custom error handler for my Express routes. Despite following the instructions in the documentation which recommend placing the custom handler at the end of the use chain, it seems that the default error ...

What could be the reason behind Typescript's unexpected behavior when handling the severity prop in Material UI Alerts?

Trying to integrate Typescript into my react project and encountering a particular error: Type 'string' is not assignable to type 'Color | undefined'. The issue arises when I have the following setup... const foo = {stuff:"succes ...

Creating a dynamic button with Vue

I need help with rendering a button on a Vue instance. I want to be able to click on the button and have another button render with a click event. When I simply mount the button, the function doesn't work. const Hello = { props: ['text'], ...

Show component featuring checkboxes

Is it possible to show a component when a checkbox is clicked? I am creating a dashboard and need to choose which tools to display on my screen. I have set up checkboxes with v-model="select", value="name of component", and used v-for for list rendering. ...

Utilizing IonPage and DeepLinkMetadataType in Ionic 3 for tab navigation: Eliminating the need for tab-0

One of the pages on my site contains tabs that are only loaded when clicked: explore.html <ion-tabs> <ion-tab [root]="tab1Root" [tabTitle]="hotTitle" tabIcon="flame"></ion-tab> <ion-tab [root]="tab2Root" [tabTitle]="searchTitle ...

Sorting tables in Vue.js with Buefy components for a user-friendly experience

In my current project, I am utilizing Vue.js. The majority of the tables I am working with use Buefy's built-in sorting feature, which I find to be the simplest solution. You can find more details in the documentation here: <template> < ...

Tips for binding two elements bidirectionally to a single date module

I am working with two date picker elements, one for selecting months and another for selecting years. I want to establish a two-way binding between these elements and a JavaScript Date object. My inquiry is as follows: Is it feasible to achieve this? If s ...

Setting up AngularJS 1.5.x to function seamlessly with SystemJS and TypeScript

I'm looking to keep all my code in AngularJS 1.x while also preparing it for an easy upgrade to AngularJS 2 in the future. To align my code with Angular 2 standards, I am interested in using TypeScript and SystemJS in version 1.5.x initially. Is ther ...

In Vue 3, the old and new values returned by a deep watcher are consistently the same

const app = { data(){ return { form: { name: '', password: '' } } }, watch: { form: { handler(form, oldForm){ console.log(form, oldForm); }, deep: true } } ...