Vue.js 3 TypeScript Refs, Complex types & Interfaces Tutorial

In this Vue tutorial we learn how to work with refs with TypeScript.

We cover how to explicit type a ref and work with complex types, interfaces and interface arrays.

Lesson Project

This lesson is a continuation of the previous. It requires an app generated by the Vue CLI with TypeScript enabled .

How to explicit type a ref

Because ref returns a reference object and not the actual value, we can’t use type assertion on refs. Instead, we have to use a generic argument.

To do that, we specify the type(s) inside a pair of open-and-close <> (angle brackets) before the parentheses.

Syntax: ref generic argument
const refName = ref<type>()

As an example, let’s define two refs. One will be typed explicitly, the other will use type inference.

Example: src/App.vue
<template>
  <p>Hello, my name is {{ name }} and I am {{ age }} years old.</p>
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue';

export default defineComponent({
  setup() {
    // inferred type
    const name = ref('John');
    // explicit type
    const age = ref<number | string>(20);

    return { name, age }
  }
});
</script>

When we run the example in the browser, everything works as expected.

If we’re using an object in a ref, the object’s properties can use type assertion.

Example: src/App.vue
<template>
  <p>Hello, my name is {{ person.firstName }} and I am {{ person.age }} years old.</p>
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue';

export default defineComponent({
  setup() {
    // objects can use type assertion
    const person = ref({
      firstName: 'Jane' as string,
      age: 19 as number | string
    })

    return { person }
  }
});
</script>

When we run the example in the browser, everything works as expected.

Complex types or interfaces

When we’re working with a more complex type like an interface, we define the types we want in the interface and then specify that interface in the generic argument of the ref.

Syntax: interface
// define the complex type
interface TypeName {
  propertyName: type
}

// specify in generic argument
ref<TypeName>({
  // match interface property
  propertyName: value
})

As an example, we’ll use the Person interface from the previous lesson.

  • src/types/Person.ts

The project should look similar to the following.

Example: project
project-name/
├── src/
|   ├── types/
|   |   └── Person.ts
|   └── App.vue

The interface will have the name and age properties with their types.

Example: src/types/Person.ts
export default interface Person {
  name: string,
  age: number
}

In the root App component, we’ll import and use the interface in the generic argument for a ref. In the ref we’ll define an object with the properties that match those in the interface.

Example: src/App.vue
<template>
  <p>Hello, my name is {{ person.name }} and I am {{ person.age }} years old.</p>
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue';
import Person from '@/types/Person';

export default defineComponent({
  setup() {

    const person = ref<Person>({
      name: 'John',
      age: 20
    });

    return { person }
  }
});
</script>

When we run the example, everything works as expected.

Complex types or interface arrays

If our complex type is an array, we add open-and-close square brackets to the generic argument.

Syntax: interface array
// define the complex type
interface TypeName {
  propertyName: type
}

// add square brackets
ref<TypeName[]>([
  { propertyName: value }
  { propertyName: value }
])

To demonstrate, let’s change our example to use an array of Person objects.

Example: src/App.vue
<template>
  <p v-for="person in people" :key="person.name">
    Hello, my name is {{ person.name }} and I am {{ person.age }} years old.
  </p>
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue';
import Person from '@/types/Person';

export default defineComponent({
  setup() {

    const people = ref<Person[]>([
      { name: 'John', age: 20 },
      { name: 'Jane', age: 19 },
      { name: 'Jack', age: 18 },
      { name: 'Jill', age: 17 }
    ]);

    return { people }
  }
});
</script>

When we run the example in the browser, everything works as expected.

Further Reading

For more information on the topics covered in this lesson, please see the relevant sections below.