Subscribe on changes!

Typescript: incorrect typings for unwrapped refs

avatar
Feb 26th 2021

Version

3.0.6

Reproduction link

https://github.com/shameleo/vue-ts-bug

Steps to reproduce

npm install
npm run dev

Open index.html browser and look console output.

What is expected?

If typescript is correct, console.log would print refs and their values.

What is actually happening?

I see values and undefined


Here essential code:

export default defineComponent({
  data: () => ({
    arr: [1, 2, 3].map((num) => ({ num: ref(num) })),
  }),
  mounted() {
    this.arr.forEach((el) => {
      console.log(el.num);
      // Typescript shows it is Ref<number>, but actually it is number
      console.log(el.num.value);
    });
  },
});
avatar
Feb 26th 2021

It's not a bug.this is intentional in options api.

you will see your expected use setup:

  setup(){
    const arr = [1, 2, 3].map((num) => ({ num: ref(num) }))
    arr.forEach((el) => {
      console.log(el.num);
      console.log(el.num.value);
    });
  }
avatar
Feb 26th 2021

If it is true than I have a lot of questions:

  1. Using refs is forbidden in optional API? I didn't see any docs or runtime warnings about it.
  2. Huge amount of tricks would be not available in optional API. Refs bound to URL params, local storage or many other use cases when some factory function returns ref or computed ref. For example, I noticed the issue, when I tried to make tree node, with checked prop as ref, which is updated when children's corresponding checked props updated. I can't do it? A HUGE dissapointment.
  3. Some libs will only can be used with composition API (for instance). And it is problem as we can't use data and methods from options API in setup, so whole component must be written in composition style.
  4. As a result, options API is second class citizen now, and it's not even matter of choice.

If 1-4 is not true, and it's actually works than why typescript should tell me it doesn't?

PS. More appropriate example of composition API would be

import { defineComponent, ref, reactive } from "vue";

export default defineComponent({
  setup() {
    const r = reactive({
      arr: [1, 2, 3].map((num) => ({ num: ref(num) }))
    });

    r.arr.forEach(el => { console.log(el.num) });
  }
});

It all about unwrapping.

PS2. Example is a bit overcomplicated (array of objects instead of simple ref) because I couldn't reproduce issue with just ref. I can now, and it's confusing. Don't know if either different IDE or different packages or whatever made the difference.

avatar
Feb 26th 2021

It should be

data: () => ({
    arr: [1, 2, 3].map((num) => ({ num })),
  }),

without ref


Please, next time consider using the forum, the Discord server or StackOverflow for questions first. But feel free to come back and open an issue if it turns out to be a bug 🙂

avatar
Feb 26th 2021

@posva, the questions is not about how to use it right, as I need ref in real case. Some usecases is in previous post. I didn't see in docs that refs can't be used in options API. Runtime does not warn if I do it. It even seems to work. Except correct typescript support. If using refs in data is forbidden, that I would expect core maintainers to explicitly say this, not some people on forum. Because it has significant consequences (look previous post) and makes options API discouraged legacy way of writing components

avatar
Feb 27th 2021

@posva He has a point. I don't think we have a rule of ref being forbidden in data. If we can solve this at the type level, we should.

avatar
Feb 27th 2021

In general, the Options API and the Composition API shouldn't be mixed. When using composition functions that return Refs, one should use setup(). This was mentioned in other comments but I couldn't find the right one, only this one about mixins.

However, you are both right about the types: it should properly infer it