What type should I use when declaring a template ref that is a single file component when using the Vue Composition API and TypeScript?

Viewed 1076

I have a single file component that I made called BusinessDetails. I'm using this component in another component, and I've attached the ref attribute to it to access its properties and methods. If I don't give the ref a type, I get an error when accessing its methods that says "Object is possibly null". I tried setting the type as BusinessComponent, typeof BusinessComponent, Vue.extend(BusinessComponent), and so on, but they all throw different errors. I eventually settled on just setting the type to any, and living with the eslint warnings. While this goes around the problem, is there was an appropriate way to do this?

<q-layout view="hHh LpR lfr">
    <q-header>
        <q-toolbar>
            <q-btn flat round @click="$router.go(-1)" icon="arrow_back" aria-label="Back"/>
            <q-toolbar-title>Create Account</q-toolbar-title>
        </q-toolbar>
    </q-header>

    <q-page-container>
        <q-page>
            <business-details ref="businessDetails" />
        </q-page>
    </q-page-container>
</q-layout>
export default Vue.extend({
    name: "CreateAccount",
    components: {
        BusinessDetails
    },
    setup () {
        const businessDetails: Ref<any> = ref(null);

        const isCurrentPageValid = (page: number): Promise<boolean> => {
            switch (page) {
                case AccountGroup.BUSINESS:
                    return businessDetails.value.isValid();
                default:
                    return Promise.resolve(true);
            }
        };

        return {
            businessDetails,
            isCurrentPageValid
        };
    }
});
3 Answers

You can use InstanceType<>:

const businessDetails = ref<InstanceType<typeof BusinessDetails> | null>(null);

This will give you typing for all the methods, etc. defined on the BusinessDetails component.

Then use it with

businessDetails.value?.isValid();

and it should just work™.

The idea for this type came from the many responses in https://github.com/vuejs/vue/issues/8721

You could use lookup types to specify the component's type like this:

type BusinessDetailsRef = Ref<
        typeof BusinessDetails['props']
      & typeof BusinessDetails['data']
      & typeof BusinessDetails['computed']
      & typeof BusinessDetails['methods']
      | null
    >

const businessDetails: BusinessDetailsRef = ref(null)

You can choose to include only the lookup types you're interested in to make it less verbose.

Verified with:

  • TypeScript 3.9.7
  • Vue 2.6.11
  • Project generated from Vue CLI 4.4.6 (w/TypeScript option selected)
  • macOS Catalina

You can use the type ComponentPublicInstance from the 'vue' package:

const businessDetails : Ref<ComponentPublicInstance | null> = ref(null);
Related