Darren Xu Blog

Using Typescript on Vue template refs


In this blog post, we’ll explore how to use TypeScript to enhance the type safety of your Vue components by leveraging template refs with the InstanceType<typeof x> operator.

Understanding Template Refs in Vue

Template refs in Vue provide a way to access and manipulate elements or components directly in the template. They are particularly useful when you need to interact with specific elements or components within a template. While template refs are powerful, they can sometimes lead to runtime errors if not handled properly. This is where TypeScript comes into play, helping us catch potential errors at compile time.

Leveraging InstanceType Operator

The InstanceType operator is a TypeScript feature that allows you to extract the instance type of a class or constructor function. In the context of Vue components, this operator becomes invaluable for ensuring type safety when working with template refs.

In the provided code snippet:

<Component
id="component-id"
:ref="e => createRef(e as InstanceType<typeof Component>, objectContainingRefs)"
/>

We’re using the :ref attribute to bind a function called createRef to the template ref of the Component element. The goal here is to use TypeScript to ensure that the createRef function receives the correct type of component instance and maintains type safety when referencing components.

Creating a Type for Object References

To further enhance type safety, we introduce the ObjectReferenceType type:

type ObjectReferenceType<T extends readonly string[]> = {
  [K in T[number]]?: InstanceType<typeof component>;
};

This type defines an object that stores references to components using keys specified in the generic type T. The type enforces that each key in the object corresponds to an instance of the specified component type.

Storing Component References

The magic happens in the createRef function:

const objectContainingRefs = ref<ObjectReferenceType<['component-id']>>({})

const createRef = <T extends readonly string[]>(
  element: InstanceType<typeof component>, 
  objectContainingRefs: ObjectReferenceType<T>
  ) => {
  objectContainingRefs.value[element.id] = element
}

This function takes an element (component instance) and an object that holds component references. By utilizing TypeScript generics, we ensure that the element parameter is of the correct component type. The function then assigns the component instance to the object’s property using the component’s ID as the key.