Object is possibly 'null'. on a ref(null)

Viewed 9075

Learning Vue Composition API (and TypeScript), from the docs I found, I should be using ref(null) to use by a sub component I have inside <template>...</template>.

This subcomponent have methods like open(), and I'm accessing it like this:

setup() {
    const subcomponentRef= ref(null);
    subcomponentRef.value.open();
    return { subcomponentRef };
}

This I agree may show the error Object is possibly 'null'. pointed to subcomponentRef.value, but the weird thing is even if I added a condition if (subcomponentRef !== null && subcomponentRef.value !== null) { ... }, it still shows that error. Why??

Also tried accessing it like subcomponentRef?.value?.open() but I receive this error Property 'open' does not exist on type 'never'..

Also tried adding a Non-null assertions, like confirmation.value!.open(); and receives same error Property 'open' does not exist on type 'never'..

Any idea what's wrong here? or maybe instead of using ref(null), I should predefine it with the actual component? but I don't have idea how to do that correctly, can't find in docs.

7 Answers

Great question! I had the same issue as you and stumbled upon this answer. What worked for me was defining the shape of the object (a typescript interface), so TS knows what is there and what isn't.

Applying this knowledge to your example:

setup() {
    const subcomponentRef = ref(null)
    subcomponentRef.value.open() // TS error here
}

Becomes:

setup() {
    const subcomponentRef = ref<null | { open: () => null }>(null)
    subcomponentRef.value?.open()
}

The TS error is now gone because:

  • Typescript knows the function open is available on subcomponentRef because we declared it upfront
  • With optional chaining we tell Typescript not to look further when subcomponentRef.value is null or undefined.

Usually these interfaces are already provided somewhere and don't need to be created manually. So in my case I just had to use the QInput interface from quasar to avoid the TS error of resetValidation not being available:

import { QInput } from 'quasar'

const driverIdInput = ref<QInput>()
driverIdInput.value?.resetValidation()

I hope this helps to clear things up and avoid these nasty errors.

Thanks for your help with this. Here's my simplified solution for focusing an input element in Vue 3 Composition API, using Typescript:

<template>
   <input ref="inputField">
</template>

const inputField = ref(null)
inputField.value.focus()      // ERROR:  TS2531: Object is possibly 'null'

Solution:

const inputField = ref<null | { focus: () => null }>(null)
if(inputField.value) {    
    inputField.value.focus()
}

Ensures a focus function is defined.

You should use the component type using typeof yourComponent or null then use ? optional chaining to access methods/properties:

setup() {
    const subcomponentRef= ref < typeof subcomponent| null > (null);
    subcomponentRef.value?.open();
    return { subcomponentRef };
}

Your basic problem is that you are not typing the ref. You always need to add a type to ref if that ref does not only have the type you use initally.

You need to add a type to the ref, assuming your component is a MySubcomponent

// In this case, you need to type
const subcomponentRef = ref<ComponentPublicInstance<typeof MySubcomponent> | null>(null);

// In this case not, type is inferred to Ref<number>
const myRef = ref(15)

Then you can use your if and access the methods of your component instance inside it. Mind that you can only access it after setup has completed, so e.g. in event listeners, the mounted hook, …

If you are here for declaring nullable number reference in vue composition API with Typescript,

setup() {
  const foo = ref<number | null>(null);

  const someAction = () => {
      foo.value = 1
  }

  return { foo, someAction };
}

On the above code snippet, you can change the number data type to match your desired data type. Also, you can add multiple possible data types if required by using the | operator.

This should be quite straightforward, you just tell TS it's in fact not null by using non-null assertion operator:

setup() {
  const foo = ref(null);

  onMounted(() => {
    foo!.someMethod();
  }

  return { foo };
}

Try any as a type if nothing works !.

setup() {

const buttonRef= ref<any>(null); // <-- highlight

const buttonClicked = () => { buttonRef.value.clicked = true }

return { buttonClicked, buttonRef }

}
Related