Subscribe on changes!

Generic array type infer incorrect

avatar
Sep 16th 2020

Version

3.0.0-rc.11

What is expected?

The parameter type of the push method of the generic array should be T not UnwrapRefSimple <T>

What is actually happening?

The parameter type is inferred to UnwrapRefSimple <T>

Steps to reproduce

image

Reproduce code ↓

import { reactive } from 'vue';

export interface QueueMethods<T> {
  add: (item: T) => void;
}

const useQueue = <T>(initialValue: T[] = []): QueueMethods<T> => {
  const queue = reactive(initialValue);
  return {
    add(value: T) {
      queue.push(value);
    },
  };
};

export default useQueue;

Caused by this line ↓

https://github.com/vuejs/vue-next/blob/master/packages/reactivity/src/ref.ts#L204-L206

Why is it that the type of Ref not inherited is also using UnwrapRefSimple to infer

avatar
Sep 16th 2020

When using ref or reactive with generics you need to cast to as Ref<T> and as reactive<T> if you sure that the type doesn't have any nested refs, because ref and reactive will unwrap nested refs automatically.

EDIT: You can test by simply doing:


const value = {
  a: ref({
    a : 1
 })
}

value.a.value.a // 1

const reactiveValue = reactive(value)

reactiveValue.a.a // 1


const valueJson = JSON.stringify(value)
const reactiveJson = JSON.stringify(reactiveValue)

valueJson !== reactive // because the reactiveValue will unwrap `a`

which makes T !== reactive<T>

avatar
Jul 6th 2021

@yuxino How did you solve this problem? i have the same thing

export const usePagination = <T>({ url }: { url: string }) => {

  const client = API

  const index = ref<T[]>([])

  const get = async (): Promise<IndexResponse<T>> => {
    const { data } = await client.get<IndexResponse<T>>(`${url}`)
    // Type 'T[]' is not assignable to type 'UnwrapRefSimple<T>[]'.
    index.value = data.data
    return data
  }
  return {
    index,
    get
  }

}
avatar
Aug 30th 2021

@yuxino How did you solve this problem? i have the same thing

export const usePagination = <T>({ url }: { url: string }) => {

  const client = API

  const index = ref<T[]>([])

  const get = async (): Promise<IndexResponse<T>> => {
    const { data } = await client.get<IndexResponse<T>>(`${url}`)
    // Type 'T[]' is not assignable to type 'UnwrapRefSimple<T>[]'.
    index.value = data.data
    return data
  }
  return {
    index,
    get
  }

}

const index = ref<T[]>([]) as Ref<T[]> can solve this problem

avatar
Oct 25th 2022

When using ref or reactive with generics you need to cast to as Ref<T> and as reactive<T> if you sure that the type doesn't have any nested refs, because ref and reactive will unwrap nested refs automatically.

Using Vue 2.7, it looks like it should be as Ref<T> for ref() and as T for reactive(), rather than as Ref<T> for ref() and as reactive<T> for reactive(). I have not tested with Vue 3 yet.