Tips for utilizing type hinting in a Vue component method with Vue 2 and Composition API

I've encountered an issue with my Vue2 application using the Composition API where TypeScript is complaining about calling a method of the Modal component without adding @ts-ignore. The components in question are <Modal> and <App>.

In the following simplified example, I have omitted app initialization:

Modal.vue

<template>
   <div ref="modalEl">I'm the modal<slot></slot></div>
</template>
<script lang="ts">
import {defineComponent, onMounted, ref} from '@vue/composition-api';
import Modal from 'bootstrap/js/dist/modal';

export default defineComponent({
    setup(props) {
       const modalEl = ref<Element | string>('');
       let modal: Modal;

       onMounted(() => {
          modal = new Modal(modalEl.value);
       });

       const show = () => modal.show();

       return {
          modalEl,
          show,
       } 
    }
});
</script>

App.vue

<template>
   <div>
       <Modal ref="modalSave">I'm modal content</Modal>
       <button @click="showModal">show modal</button>
   </div>
</template>
<script lang="ts">
import {ref,defineComponent} from '@vue/composition-api';
import Modal from 'Modal.vue';
export default defineComponent({
  components: {
      Modal
  },
  setup(props) {
      const modalSave = ref<InstanceType<typeof Modal> | null>(null);
      const showModal = () => modalSave.value?.show();

      return {
          modalSave,
          showModal
      }
  }
});

</script>

Although everything seems to work fine, TypeScript throws an error during compilation:

ERROR in App.vue.ts(115,24) TS2339: Property 'show' does not exist on type '{ readonly $el: Element; readonly $options: { data?: DefaultData | undefined; props?: string[] | { [x: string]: Prop | { type?: Prop | Prop[] | undefined; required?: boolean | undefined; default?: any; validator?: ((value: any) => boolean) | undefined; } | Prop<...>[]; } | undefined; ... 35 more ...'.

If anyone has suggestions on how to resolve this TypeScript error without resorting to ts-ignore, please share your insights. Thank you!

Answer №1

To implement this functionality, you will have to utilize the defineExpose method as outlined in the documentation:

<!-- MyModal.vue -->
<script setup lang="ts">
<template>
   <div ref="modalEl">I am the modal content<slot></slot></div>
</template>
<script lang="ts">
import {defineComponent, onMounted, ref} from '@vue/composition-api';
import Modal from 'bootstrap/js/dist/modal';

export default defineComponent({
    setup(props) {
       const modalEl = ref<Element | string>('');

       onMounted(() => {
          modal = new Modal(modalEl.value);
       });

       const displayModal = () => modal.show();

       defineExpose({ displayModal }); 👈

       return {
          modalEl,
          displayModal,
       } 
    }
});
</script>
</script>

Answer №2

My breakthrough came when I successfully implemented (global) augmentation by saving the code in a *.d.ts file. While it may not be the most optimal solution, it serves as a temporary fix until the app transitions to vue3. This approach offers a straightforward resolution without the need for extensive rewriting:

declare module 'vue/types/vue' {
    interface Vue {
        show: () => void
    }
}

export {}

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

Splitting For Loop Buttons into Separate Rows in a Table in Vue

My first table row uses a for loop to create 5 buttons with different values. I am looking for a way to separate these buttons so that the second button appears in the second <tr> and replaces button2, while the next one appears in the third <tr&g ...

What is the best approach to locally extending a global interface in TypeScript?

Can someone explain the concept of extending a global interface locally as mentioned in this GitHub post? I find it difficult to grasp the idea due to lack of explanation in the post. The post in question is as follows. you can extend a global interface ...

What is the reason for polyfilling private fields (#field) in JavaScript on angular builds?

I was eager to incorporate JavaScript private fields into my Angular (v17) application built with Angular CLI. I specified the target as ES2022 in the tsconfig, but after building the app, I discovered that my private field had been poly-filled with a We ...

Expanding a Typescript generic class by adding abstract methods

Struggling to expand an abstract generic class and encountering issues with extending certain methods. Take a look at this: abstract class A<T,K> { protected abstract upload<S>(item: T): S protected abstract download(item: T): K } ...

Serving HTML from NodeJS instead of JSON

I have implemented two middleware functions import { NextFunction, Request, Response } from 'express'; const notFoundHandler = (req: Request, res: Response, next: NextFunction) => { const error = new Error(`Page Not Found - ${req.originalUr ...

Error thrown by computed property despite its functionality

I have a code snippet that retrieves data from an external server, which is triggered when the component is mounted var get_one = 'https://api.example.com/rb.php'; axios.get(get_one) .then(response => { ...

Encountering the error message "This expression cannot be invoked" within a Typescript React Application

I'm working on separating the logic from the layout component in my Typescript React Application, but I suspect there's an issue with the return type of my controller function. I attempted to define a type to specify the return type, but TypeScr ...

Error: Attempting to access a property called 'sign' on an undefined value

I encountered an issue while signing my transaction where I received an error message stating sendTransaction needs signer. Even though both message (encrypted using keccak256) and signer have values, I am unsure why there is a problem when executing the w ...

Custom buttons for adding and deleting items on Bootstrap Vue Table

I am interested in developing a dynamic bootstrap table similar to the one showcased here. Currently, I am utilizing the following code to exhibit data from an array within the bootstrap table: <b-table striped hover :items="shotlist_tab.shots">< ...

Creating a live dashboard in Laravel

I am currently working on creating a dashboard to monitor the status of IoT devices in Laravel. The front-end is built using Vue.js to call the Laravel API every minute, which in turn accesses the AWS IoT shadow. View the architecture diagram here Howev ...

Tips for sending user IDs through an Angular 8 interceptor

Alright, In my Angular application that is using version 8, I have an HttpMaintenanceInterceptor configured without the use of cookies. Instead, I have a getAccessToken method within the authService as shown below: getAccessToken(): string { return ...

Removing a node from PrimeNg tree component when filtering and dropping

Hey there, I'm experiencing an issue with dragging and dropping from a tree while filtering. It seems that when I drag and drop without using the filter, the item is removed from the right tree. However, when I apply the filter, the item doesn't ...

What is the process for sending a POST request from a plain JavaScript application?

I've developed a stand-alone webpage named index.html that includes a basic form with an input field. Every time I attempt to submit the form by clicking the button, I intend to send a POST request to my server. Unfortunately, the browser keeps displa ...

A class member is not permitted to include the 'const' keyword

Having trouble with my ts code as I try to create a constant that can be easily changed by just modifying a simple element - but keep encountering the error that says "A class member cannot have the 'const' keyword." Here's the code snippet ...

What methods can be used to prevent external URLs from being crawled by using hashing?

Recently, I came across a post on a coding forum discussing how to hide links from Google using JavaScript. The idea is to mask the URLs from being crawled by Google while still making them accessible to users. In my case, I have external URLs that I want ...

Determine the data type of an index within an array declaration

Imagine we have the following array literal: const list = ['foo', 'bar', 'baz'] as const; We are attempting to create a type that represents potential indices of this array. This is what we tried: const list = ['foo&ap ...

The validation for decimal numbers fails to function when considering the length

I've been struggling to come up with a regular expression for validating decimal numbers of a specific length. So far, I've tried using pattern="[0-9]){1,2}(\.){1}([0-9]){2}", but this only works for numbers like 12.12. What I'm aimin ...

Can you explain the significance of the 'p' parameter in the p5.js code function for sketch-rnn?

The repository does not appear to include any code that defines the variable p, which leaves me uncertain about its purpose. What is the significance of the 'p' parameter in the function, and why is it needed? The general structure of the code i ...

Ion-List seamlessly integrates with both ion-tabs and ion-nav components, creating a cohesive and dynamic user interface

On my homepage, there is an ion-list. Sometimes (not every time), when I select an item in this list or navigate to the register page using "this.app.getRootNav().push("ClienteCadastroPage")", and then select an input in the registerPage or descriptionPage ...

Tips for postponing 'not found' error until the data has finished loading in Vue.js

I am accessing the following route: http://site.dev/person/1 The structure of my component is as follows: // PeopleComponent.vue <template> <div> <template v-if="person == null"> <b>Error, person does not exist.</b& ...