41
votes

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>
9
You can mark the type as HTMLInputElement - Ettore Raimondi

9 Answers

45
votes

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

31
votes

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>
  }
}
7
votes

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

4
votes

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();
3
votes

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.

1
votes

Maybe it will be useful to someone. It looks more beautiful and remains type support.

HTML:

<input ref="inputComment" v-model="inputComment">

TS:

const inputValue = ((this.$refs.inputComment as Vue).$el as HTMLInputElement).value;
1
votes

In case of custom component method call,

we can typecast that component name, so it's easy to refer to that method.

e.g.

(this.$refs.annotator as AnnotatorComponent).saveObjects();

where AnnotatorComponent is class based vue component as below.

@Component
export default class AnnotatorComponent extends Vue {
    public saveObjects() {
        // Custom code
    }
}
0
votes

I found a way to make it work but it is ugly in my opinion.

Feel free to give other/better suggestions.

update() {
    this.$emit('input', {
        plate: (<any>this.$refs.plate).value,
    });
}
0
votes

Make sure to wrap your exports with Vue.extend() if you are converting your existing vue project from js to ts and want to keep the old format.

Before:

enter image description here

<script lang="ts">

export default {
  mounted() {
    let element = this.$refs.graph;

...

After:

enter image description here

<script lang="ts">

import Vue from "vue";

export default Vue.extend({
  mounted() {
    let element = this.$refs.graph;

...