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.
const refName = ref<type>()
As an example, let’s define two refs. One will be typed explicitly, the other will use type inference.
<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.
<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.
// 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.
project-name/
├── src/
| ├── types/
| | └── Person.ts
| └── App.vue
The interface will have the name and age properties with their types.
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.
<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.
// 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.
<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.