Vuejs typescript this.$refs.<refField>.value does not exist

56,046

Solution 1

You can do this:

class YourComponent extends Vue {
  $refs!: {
    checkboxElement: HTMLFormElement
  }

  someMethod () {
    this.$refs.checkboxElement.checked
  }
}

From this issue: https://github.com/vuejs/vue-class-component/issues/94

Solution 2

Edit - 2021-03 (Composition API)

Updating this answer because Vue 3 (or the composition API plugin if you're using Vue 2) has some new functions.

<template>
  <div ref="root">This is a root element</div>
</template>

<script lang="ts">
  import { ref, onMounted, defineComponent } from '@vue/composition-api'

  export default defineComponent({
    setup() {
      const root = ref(null)

      onMounted(() => {
        // the DOM element will be assigned to the ref after initial render
        console.log(root.value) // <div>This is a root element</div>
      })

      return {
        root
      }
    }
  })
</script>

Edit - 2020-04:

The vue-property-decorator library provides @Ref which I recommend instead of my original answer.

import { Vue, Component, Ref } from 'vue-property-decorator'

import AnotherComponent from '@/path/to/another-component.vue'

@Component
export default class YourComponent extends Vue {
  @Ref() readonly anotherComponent!: AnotherComponent
  @Ref('aButton') readonly button!: HTMLButtonElement
}

Original Answer

None of the above answers worked for what I was trying to do. Adding the following $refs property wound up fixing it and seemed to restore the expected properties. I found the solution linked on this github post.

class YourComponent extends Vue {
  $refs!: {
    vue: Vue,
    element: HTMLInputElement,
    vues: Vue[],
    elements: HTMLInputElement[]
  }

  someMethod () {
    this.$refs.<element>.<attribute>
  }
}

Solution 3

This worked for me: use (this.$refs.<refField> as any).value or (this.$refs.['refField'] as any).value

Solution 4

son.vue

const Son = Vue.extend({
  components: {},
  props: {},
  methods: {
    help(){}
  }
  ...
})
export type SonRef = InstanceType<typeof Son>;
export default Son;

parent.vue

<son ref="son" />

computed: {
  son(): SonRef {
    return this.$refs.son as SonRef;
  }
}

//use
this.son.help();

Solution 5

Avoid using bracket < > to typecast because it will conflict with JSX.

Try this instead

update() {
    const plateElement = this.$refs.plate as HTMLInputElement
    this.$emit('input', { plate: plateElement.value });
}

as a note that I always keep remembering

Typescript is just Javascript with strong typing capability to ensure type safety. So (usually) it doesn't predict the type of X (var, param, etc) neither automatically typecasted any operation.

Also, another purpose of the typescript is to make JS code became clearer/readable, so always define the type whenever is possible.

Share:
56,046
Rick
Author by

Rick

Passionate and enthusiastic software developer. Most interested in c# and angular.

Updated on July 24, 2022

Comments

  • Rick
    Rick almost 2 years

    While rewriting my VueJs project in typescript, I came across a TypeScript error.

    This is a part of the component that has a custom v-model.

    An input field in the html has a ref called 'plate' and I want to access the value of that. The @input on that field calls the update method written below.

    Typescript is complaining that value does not exist on plate.

    @Prop() value: any;
    
    update() {
        this.$emit('input',
            plate: this.$refs.plate.value
        });
    }
    

    template:

    <template>  
    <div>
        <div class="form-group">
            <label for="inputPlate" class="col-sm-2 control-label">Plate</label>
    
            <div class="col-sm-10">
                <input type="text" class="form-control" id="inputPlate" ref="plate" :value="value.plate" @input="update">
            </div>
        </div>
    
    </div>
    </template>
    
  • pranavjindal999
    pranavjindal999 about 6 years
    I'm using let mycomp = Vue.extend({}) syntax. How can I do the same in that? Also should I use the above syntax?
  • George
    George about 6 years
    You could create an interface that represents the type of refs, and then cast to that. At least then you're avoiding the any, and your view will be type checked against it.
  • pranavjindal999
    pranavjindal999 about 6 years
    I'd need to cast everytime i want to use that ref or can I somehow write it once?
  • George
    George about 6 years
    This is generally why people use vue-class-component: classes are better suited to defining types than objects as with objects it's harder (still possible I believe) to specify component-wide types.
  • pranavjindal999
    pranavjindal999 about 6 years
    Hmm thanks.. I actually hate decorators in vue class components.
  • George
    George about 6 years
    Any particular reason why? This problem wouldn't require a decorator - you'd just need to put the component decorator on the class and then declare a variable $refs of your interface type.
  • ffxsam
    ffxsam almost 6 years
    This doesn't work. Maybe it used to, but not today (using vue-cli 3).
  • George
    George almost 5 years
    Casting to any isn't type safe - better to define $refs like in the answer below :)
  • Jens
    Jens over 4 years
    should be (this.$refs['refField'] as any).value
  • walnut_salami
    walnut_salami over 4 years
    I needed to write $refs!: (notice the exclamation mark) in order for it to work, but I'm using vue-property-decorator. @George could you please edit the answer if my fix is ok? Don't wanna break it for class component users as maybe there's some difference there (EDIT: ah actually the exclamation mark is in the link you posted)
  • John Snow
    John Snow about 4 years
    This clears the error but defeats the benefits of using typescript in the first place.
  • AverageHelper
    AverageHelper over 3 years
    It may be more appropriate to cast to unknown, and then cast or type-guard to whatever else you need. unknown is more permissive, but still does the usual type checks, and is therefore much more safe.
  • UndeadKernel
    UndeadKernel about 3 years
    Fantastic way to reference the types of custom components. This way also allows me to avoid using the class decorators.
  • d9k
    d9k about 3 years
    looks cool but methods names autocomplete doesn't work for me in PhpStorm :( import VForm from 'vuetify/src/components/VForm/VForm.ts'; type RefVForm = InstanceType<typeof VForm>;
  • user2471801
    user2471801 about 3 years
    This lead me to the right direction. I'm new to vuejs, and don't quite understand the $ref usage, so for others like myself: @Ref('aButton') readonly button!: HTMLButtonElement allows you to access the element where <button ref="aButton" ... /> from within your script as this.button.
  • Vitor Braga
    Vitor Braga about 3 years
    InstanceType<typeof Son> is what I needed. Thanks
  • Andrew Koster
    Andrew Koster almost 3 years
    There's no need to cast anything. A ref is of type Vue | Element | Vue[] | Element[], and if that's not specific enough, you can use the instanceof operator to fix type errors and throw exceptions or run loops or whatever else you need to do based on the specific return type at runtime.
  • ortonomy
    ortonomy over 2 years
    YES! this is exactly what is missing from the official docs. Why would I want to use a class component? The options API is much better...
  • Rudey
    Rudey over 2 years
    const root = ref(null) How can the TypeScript compiler possibly know what the type of root is here? Don't you have to specify the type (HTMLDivElement) explicitly?
  • John Snow
    John Snow over 2 years
    Yes, I believe that vue's composition api is typed such that ref(null) is really ref<HtmlElement|null>(null) or something like that
  • Mithsew
    Mithsew about 2 years
    in case of using options API with defineComponent it is necessary to use as typeof Component
  • Joe Maffei
    Joe Maffei almost 2 years
    This approach gave me an error: Property '$refs' will overwrite the base property in 'BasePublicView... If this is intentional, add an initializer. Otherwise, add a 'declare' modifier or remove the redundant declaration.. Changing it to declare $refs: { fixed it.