Search code examples
typescriptvue.js

How to avoid unsafe assignment from Ref<string> to Ref<string | undefined>?


Vue's ref function can create a typed Ref object, and the value type can optionally contain undefined. Is there a way to set types so that assigning a Ref<string> to Ref<string | undefined> is prevented by Typescript?

Example

Here I create a Ref<string> and try to assign undefined, which (as expected) causes an error:

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, I can assign this ref to another ref supporting undefined, which erases 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

I encountered this problem while writing a function which should (among other things) unset the value of a ref. I'd like to have TS support for only passing 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

Solution

  • You can make it generic and check if the ref's type includes undefined, and if it doesn't include undefined, then disallow the call by changing the type to never:

    function unsetRef<T extends Ref<unknown>>(r: undefined extends T["value"] ? T : never) {
    
    const s1 = ref('hello');
    
    const s2 = ref<string | undefined>("hello")
    
    unsetRef(s1); // error
    unsetRef(s2); // okay
    

    Playground