What is the proper way to declare static references in the Composition API with Typescript?

Currently, I am using the Composition API (with <script setup lang='ts'>) to create a ref that is utilized in my template:

const searchRef = ref(null)
onMounted(() => { searchRef.value.focus() })

Although it works and my code compiles without any errors, my IDE (either JetBrains Goland or Webstorm) displays a warning with

TS2531: Object is possibly 'null'.
.

The more drastic solution involves using

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Object is possibly 'null'.

However, I'm interested in finding a better approach if possible.

I had initially hoped that utilizing optional chaining would resolve the issue:

const searchRef = ref(null)
onMounted(() => { searchRef.value?.focus() })

Despite still compiling and functioning correctly, this implementation now triggers an error message of

TS2339: Property 'focus' does not exist on type 'never'.

What would be the proper way to tackle this particular dilemma?

Answer №1

When using template refs, it's important to specify a generic type argument for the ref during initialization. The specified generic type should match the type of the targeted element in the template reference.

Below are examples demonstrating how to use template refs with native HTML elements:

const htmlElemRef = ref<HTMLElement>()         // => type: HTMLElement | undefined
const inputRef = ref<HTMLInputElement>()       // => type: HTMLInputElement | undefined
const textareaRef = ref<HTMLTextAreaElement>() // => type: HTMLTextAreaElement | undefined

While initializing with null is optional, omitting it can lead to more concise code. In this case, the resulting type becomes <T | undefined>, which accurately reflects that the ref will be

undefined</code if the targeted element isn't present in the template structure. Optional chaining should still be used when accessing the value of the <code>ref
since it might be undefined.

For example:

const searchRef = ref<HTMLInputElement>()
onMounted(() => { searchRef.value?.focus() })

As mentioned by @Kurt, Vue components require a different approach when working with template refs. For these scenarios, the generic type for the ref should be an InstanceType of the component's type (obtained from a .vue import) (documentation):

InstanceType<typeof MyComponentDefinition>

For instance:

import HelloWorld from '@/components/HelloWorld.vue'

const helloRef = ref<InstanceType<typeof HelloWorld>>()

Please note that if you're working with TypeScript files and Volar instead of Vue Single File Components, make sure to enable Volar's Takeover Mode.

demonstration link

Answer №2

To address this issue, one recommended solution is to manually include the appropriate typings:

const searchElementRef: Ref<HTMLElement | null> = ref(null);
onMounted(() => { searchElementRef.value?.focus() });

Please be aware that the type HTMLElement may vary if the reference is pointing to a Vue Component (e.g.

Ref<HTMLElement | Vue | null>
).

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Vue component fails to react to updates from Vuex

Currently, I am developing a system to facilitate the management of orders at a shipping station. Although I have successfully implemented the initial changes and most of the functionality, I am encountering an issue where one component fails to update ano ...

Discover the latest DOM elements using Webdriverio 5

Currently, I am developing a REact based CMS application that features a form with multiple carousels. I am encountering an issue where the webdriverio implementation is unable to locate an element, even though the same xpath works fine when tested manua ...

Logging in with oidc-client and IdentityServer4 on separate domains

I am currently working on a VueJs application hosted on localhost which utilizes the oidc-client.js library for logging in to an IdentityServer4 server located in a production environment on another domain. Upon successful login, I am redirected back to t ...

TSLint Alert: Excessive white space detected before 'from' keyword (import-spacing)

I'm currently using WebStorm and working to maintain a specific code style: However, I encounter an issue where TSLint highlights my spacing and provides the following hint: "Too many spaces before 'from' (import-spacing)". My main ques ...

What is the simplest method for converting a large JSON array of objects into an object containing the same data under the key "data" in TypeScript?

What is the most efficient method for converting a large JSON array of objects into an object with a key named "data" using TypeScript: Original Format: [ { "label":"testing", "id":1, "children":[ { "label":"Pream ...

npm-bundle encounters an issue with Error: ENOENT when it cannot find the file or directory specified as 'package.json'

npm-bundle is throwing an error that says Error: ENOENT: no such file or directory, open 'package.json' in my NodeJs project. It works fine if I manually create test.js and package.json, then run npm install followed by npm-bundle. However, when ...

Transforming the Blade user profile into a sleek Vue interface for the Laravel-Vue project

They have requested me to convert all pages from blade to vuejs. I have begun the process with the user profile (Profile.vue), but I am unsure about how to execute PUT requests using Axios in this case. Can someone provide guidance on creating the code for ...

A TypeScript array interface featuring an indexed structure along with the ability to access custom properties through string keys

I am looking to create an array of objects in which each object is indexed by numbers and can also be grouped under a specific key. Here's what I have so far: const myArray:ICustomArray = [] myArray.push(item) myArray[item.key] = item; However, I a ...

Can the parameters in a .slice() be customized within a v-for loop?

I am currently working with Laravel 8 and using blade syntax. The following code snippet is from a Vue component I created: <li class="w-3/12 md:w-auto pt-0 md:px-4 md:pt-2 md:pb-0 list-none relative" v-if="comic.item_type === 'b&ap ...

The Nuxt Auth local token cookie enables the user's status to be marked as logged in

At the moment, I am utilizing Nuxt Auth for managing logins and sessions in my application. Everything is functioning smoothly so far. However, I seem to be facing an issue with how cookies are being handled in my setup. I'm not entirely sure where th ...

Is there a way to find the recursive key types in TypeScript?

Is there a method to ensure that code like this can compile while maintaining type safety? type ComplexObject = { primitive1: boolean; complex: { primitive2: string; primitive3: boolean; } }; interface MyReference { myKey: keyof ComplexObj ...

Newbie: Troubleshooting Vue Errors - "Vue is not recognized"

I'm currently at the beginning stages of learning Vue and am practicing implementing it. However, I keep encountering the errors "vue was used before it was defined" and "Vue is not defined". Below are excerpts from my HTML and JS files for reference. ...

Troubleshooting: Issues with Angular 2 Dependency Injection

I am facing an issue with my angular2 application that involves a simple dependency injection, but it seems to be not working correctly. Can anyone help me figure out what might be missing? Below is the error message I am encountering: EXCEPTION: Cannot ...

What steps are involved in generating a Typescript module definition for a directory containing a babel-plugin-content-transformer?

Currently utilizing the babel-plugin-content-transformer to import a directory containing YAML documents in a React Native/Expo project. The configuration for my babel plugin looks like this: ['content-transformer', { transformers: [{ ...

Is there a way to implement error validation successfully in React Hook Form while utilizing template literals within the register function?

Utilizing React Hook Form along with Typescript, I am in the process of constructing a series of forms using a configuration object. Within this configuration object, there exists a key named prop which is of type string and is being passed to the register ...

The Selenium test functions correctly in the production environment, however it encounters issues when run

I am facing an issue with my Vue.js application while writing Selenium tests. The test passes when run against the deployed production version of the app, but fails when run against a local version. When running the app locally, it is not from a build, bu ...

What is the best way to transfer data from a component to a .ts file that contains an array?

I am currently developing a budgeting application and I have a component that is responsible for holding values that I want to pass to an array stored in a separate file. While I can retrieve data from the array, I am facing difficulty in figuring out how ...

Unable to refresh the context following a successful API call

My current project in NextJS requires a simple login function, and I have been attempting to implement it using the Context API to store user data. However, I am facing an issue where the context is not updating properly after fetching data from the back-e ...

Comparing Input and Output Event Binding

Can you provide reasons why using @Output for events is more advantageous than passing an @Input function in Angular 2+? Utilizing @Input: Parent Template: <my-component [customEventFunction]=myFunction></my-component> Inside parent-compone ...

Updating a component's properties for a dynamic router-link in Vue.js: Tips and tricks

Just diving into Vue and haven't come across anything that directly addresses my current issue. I'm working on a simple Vue app with VueRouter, aiming to create a bracket-style sports tournament that keeps track of game outcomes in the tournamen ...