overdub60
overdub60

Reputation: 738

Vue & Typescript: How to avoid TS error when accessing child component's methods

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?

Upvotes: 9

Views: 6967

Answers (4)

Reynicke
Reynicke

Reputation: 1550

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
}

Upvotes: 12

x6ud
x6ud

Reputation: 11

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();
        }
    }
}){}

Upvotes: 0

LLai
LLai

Reputation: 13406

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.

Upvotes: 2

carlosza
carlosza

Reputation: 375

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

Upvotes: 1

Related Questions