What are the best practices for preventing risky assignments between Ref<string> and Ref<string | undefined>?

Is there a way in Typescript to prevent assigning a Ref<string> to Ref<string | undefined> when using Vue's ref function to create typed Ref objects?

Example

When trying to assign undefined to a Ref<string>, an error is expected:

const s1 = ref('hello') // typed as Ref<string>
s1.value = undefined // causes a TS error as expected: "Type 'undefined' is not assignable to type 'string'"

However, it seems possible to assign this reference to another one that supports undefined, bypassing the type check:

const s2: Ref<string | undefined> = s1 // IMO unsafe assignment -- how to make this illegal?
s2.value = undefined
const s: string = s1.value // TS thinks that the value is still a string, but actually it's undefined

My use case

This issue came up while working on a function that should unset the value of a ref. I would like Typescript support for only accepting refs with valid types:

function unsetRef<T>(r: Ref<T | undefined>) {
  r.value = undefined
}

unsetRef(s1) // expected to be ok
unsetRef(s2) // expected to fail, but causes no TS error

Answer №1

To create a more generic approach, you can verify if the type of the reference includes undefined. If it does not include undefined, then prevent the call by changing the type to never:

function removeRef<T extends Ref<unknown>>(r: undefined extends T["value"] ? T : never) {
const ref1 = ref('hello');

const ref2 = ref<string | undefined>("hello")

removeRef(ref1); // error
removeRef(ref2); // okay

Code Playground

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

What's causing the failure in the execution of the "Verify version" step?

Upon reviewing the 3DSecure GlobalPay documentation, my team decided to integrate it using JSON, incorporating our own client-side implementation. This decision was made because we already have another integration with a different 3DS verification service ...

Error: Parameter 'click' is not defined in Vue

Where Did I Go Wrong? This is what I have in my code: <stepTitle number=1 @click.native="setStep(number)" :class=" step === 1 ? 'active' : 'un-active' " title="Let's get started"/> And in the methods section: methods: { ...

What is the process for testing an iframe and retrieving all of the response headers?

I'm currently working on a web application that can display URLs in an iframe. However, I also want to be able to test the URL before showing it in the iframe. The goal is to wait for a response and only display the iframe if there are no errors or if ...

Using vuex-class to interact with Vuex in non-Vue components

Is it possible to access Vuex outside of a Vue component using vuex-class? In a typical scenario, the process is quite straightforward: // some JS file import store from './../store'; // path to Vuex store store.commit('ux/mutationName&ap ...

``There seems to be an issue with the Deno logger FileHandler as it

I am currently in the process of setting up loggers for an application. I have a handler named console which logs every event to the console. Additionally, there is a handler called app that is supposed to log all events to a file. While the logs are succ ...

What is the best way to associate a style with my v-for loop in this specific instance?

Why is there no styling applied to the td element when using steps.name? The code seems correct and I see no errors in the dev tools. <!-- Template --> <td v-for="steps in item.steps" v-bind:style="steps.name"> {{ steps.name }} </td> / ...

The specified element "errors" is not found in the VeeValidate template

Trying to use VeeValidate for field validation in a Vue form with Vue 2.5 and VeeValidate 2.1, the following method is used: <input class="form-control" name="contact-email" id="contact-email" type="email" v-model="contact-email" v-validate="'re ...

TypeScript encounters a self-referencing type alias circularly

Encountering an issue with Typescript 3.6.3, specifically getting the error: Type alias 'JSONValue' circularly references itself. View code online here In need of assistance to resolve the circular reference in this specific version of TS (note ...

Issue with typing error in datetime.d.ts file that is missing

I initially installed the typings for luxon using npm install --save-dev @types/luxon. However, upon further evaluation, I realized that it was unnecessary and decided to remove it manually: deleted the folder node_modules/@types/luxon removed entries in ...

extracting the HTML content from JavaScript and saving it into a standalone file

update When I click a link, a popup opens and I see all this HTML. The smile method is called when I click the link, and we append HTML in that method so that we can see it when the popup is opened. I moved it to a separate file something.component.html, ...

Manipulating prop values through dropdown selection

I'm currently working on implementing filtering based on a prop value that changes according to the dropdown selection. Here's my progress so far: template(v-for="field in tableFields") th(:id="field.name") select(@change="filterScope(sc ...

Connecting a hybrid/web client application to established remote web services outlined through a set of WSDL specifications

Summarizing the Problem I am faced with the task of integrating a mobile hybrid application, likely built on Ionic, that will need to indirectly consume several SOAP web services. My goal is for the TypeScript client in the mobile app to have knowledge of ...

Minimize the number of axios requests made every time a Vue component is displayed

I am relatively new to Vue, so please bear with me as I may not have all the knowledge in this area. Within one of my child components (Product_Collection.vue), I am making an axios request to retrieve products from my Shopify store using their GraphQL ...

Confounding Typescript Type Bindings

I am facing an issue with my Typescript component that uses react-jss and the classes object for styling. The error message I'm getting is: Binding element 'classes' implicitly has an 'any' type., and I'm struggling to find a ...

Getting a callback from an event listener in Nuxt/Vue: A step-by-step guide

I am currently using Nuxt version 2.15.8 and I am looking for a way to retrieve the result of an emitted event. In my setup, there is a child component that emits the event, then the parent component receives it and makes API calls based on it. I want to e ...

Vue.JS - Dynamically Displaying Property Values Based on Other Property and Concatenating with

I have a reusable component in Vue.js called DonutChart. <donut-chart :chartName="graphPrefix + 'PerformanceDay'" /> The value of the property graphPrefix is currently set to site1. It is used as part of the identifier for the div id ...

Angular - Ensuring service completion before proceeding with navigation

I'm currently facing an issue where I need to populate data in a service before navigating, but the navigation is happening before the data is ready. Here's the code in my service: addToken(token) { this.cookieService.set( 'token', ...

Module 'angular2/angular2' not found

Currently, I am working on a node application with angular2 and gulp. One of the components I have created is login.ts: import {Component, View} from 'angular2/angular2'; import {FormBuilder, formDirectives } from 'angular2/forms'; @C ...

A guide on utilizing ESLint to lint expressions within Vue template code

There are some JavaScript/TypeScript expressions in Vue's template, such as {{ a.b }} and v-if="a === -0". I wish for ESLint to be able to lint these expressions in the template. In the image provided, TypeScript code within <script setup ...

What is the importance of using graphql-tag in conjunction with Apollo?

After following various tutorials and examples, I successfully incorporated a GraphQL API into a basic Vue application. To interact with the API, I am utilizing Apollo and graphql-tag's template literal to compose queries. Here is an example: gql` ...