9

I've created a custom class-based vue component and I'm trying to access its methods and/or computed properties from a parent component. There is an example in the Vue Docs that explains what I'm trying to do (https://v2.vuejs.org/v2/guide/components-edge-cases.html#Accessing-Child-Component-Instances-amp-Child-Elements). So basically it's this

class ParentComponent extends Vue {
    someMethod() {
        (this.$refs.myChildRef as ChildComponent).focus()
    }
}

class ChildComponent extends Vue {
    focus() {
        // do something
    }
}

Now, this leads to a TS error: "TS2339: Property 'focus' does not exist on type 'Vue'"

So apparently, typescript doesn't see that my ChildComponent has additional methods.

The code still works during runtime though, so it just appears to be a typescript issue.

Does anyone have an idea how to solve this?

4 Answers 4

12

Option 1: Ignore it

//@ts-ignore

Option 2: Type any

const child: any = this.$refs.myChildRef;
child.focus();

Option 3: Interface, as mentioned by @LLai

interface ComponentInterface {
    focus: () => void
}

Option 4: Merge types

As a one liner as @LaLai says

(this.$refs.myChildRef as ChildComponent & { focus: () => void }).focus()

or if you need it more often

Class ParentComponent extends Vue {
    $refs: Vue["$refs"] & {
      myChildRef: { focus: () => void }
    };  
}

Option 5: @Ref() decorator from vue-property-decorator

Class ParentComponent extends Vue {
  @Ref()
  childComponent: ChildComponent
}
Sign up to request clarification or add additional context in comments.

2 Comments

An interface might be the best option. I don't get it though. It's not a problem in React, I've only had this issue with Vue. Even if I do the following, I get the same error: import ChildComponent from 'ChildComponent.vue' const c = new ChildComponent(); c.focus(); // TS error
Yes, Vue is a bit strange in this regard. Maybe in future releases it will properly detect the methods in $refs, but for now $refs is defined as $refs: { [key: string]: Vue | Element | Vue[] | Element[] } So if you have your Interface you need to call your method with (this.$refs. myChildRef as ComponentInterface).focus();
2

One solution is to implement an interface on your child component.

interface ComponentInterface {
    focus: () => void
}

class ChildComponent extends Vue implements ComponentInterface {
    focus () {
        // do something
    }
}

Another option is to merge the types

class ParentComponent extends Vue {
    someMethod() {
        (this.$refs.myChildRef as ChildComponent & { focus: () => void }).focus()
    }
}

But this can get repetitive if you are calling focus a lot outside of the child component. I prefer option 1.

Comments

1

We have similar issues with our components which were built using typescript. The solution we are using for now, is just adding a ts-ignore comment before each of issue. It's not the best way but it works.

//@ts-ignore

try it out

Comments

0

I don't want to declare the method name twice so I use this way:

In ChildComponent.ts

import Vue from 'vue'

export default class ChildComponent extends Vue.extend({
    methods: {
        focus() {}
    }
}){}

In ChildComponent.vue

<template>...</template>

<script lang="ts">
    import ChildComponent from './ChildComponent'

    export default ChildComponent;
</script>

In ParentComponent

import Vue from 'vue'

import ChildComponent from './ChildComponent.vue'
import ChildComponentClass from './ChildComponent'

export default class ParentComponent extends Vue.extend({
    components: {ChildComponent},
    methods: {
        someMethod() {
            (<ChildComponentsClass>this.$refs.child).focus();
        }
    }
}){}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.