What's Vue 3 composition API's type-safe way of defining methods

Viewed 1162

I'm using Vue's composition API (in Vue.js 3) and build my component logic mostly within setup(). While accessing my own props via setup(props) is straight forward, I'm unable to expose the functions defined here as methods in a type-safe manner.

The following works, but I need the any-cast, since there's no method interface exposed to TypeScript.

<!-- MyComponent.vue -->

<script lang='ts'>
// ...
export default defineComponent({
  setup() {
    // ...
    return {
       publicFunction: async (): Promise<void> => { /* ... */ };
    }
  }
});

</script>
<!-- AppComponent.vue -->

<template>
  <MyComponent ref="my"/>
</template>

<script lang='ts'>

export default defineComponent({
  async setup() {
    const my = ref();

    async func() {
      await (my.value as any).publicFunction(); // <-- gross!
    }

    return { my, func };
  }
});

</script>

Defining my function in methods is not an option, since that way, I wouldn't be able to access it from setup.

1 Answers

You're looking for InstanceType.

import MyComponent from 'path/to/the/component';

export default defineComponent({
  async setup() {
    const my = ref<InstanceType<typeof MyComponent>>();

    async func() {
      await my.value?.publicFunction();
    }

    return { my, func };
  }
});

One caveat with this approach is that (as you can see) you'd have to use an optional chaining or non-null assertion operator, unless you pass an initial/default instance as an argument to the ref() function; otherwise, TypeScript is going to mark it as "possibly undefined".

In most cases, if you are certain that it's always going to be defined, you could type cast it to an empty object with the as-syntax.

const my = ref({} as InstanceType<typeof MyComponent>);

async func() {
  await my.value.publicFunction();
}
Related