Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nested array refs type errors #11548

Open
cnzgray opened this issue Aug 7, 2024 · 7 comments
Open

Nested array refs type errors #11548

cnzgray opened this issue Aug 7, 2024 · 7 comments

Comments

@cnzgray
Copy link

cnzgray commented Aug 7, 2024

Vue version

3.4.35

Link to minimal reproduction

https://play.vuejs.org/#eNp9kcFuwjAMhl/FyoVVQi2U7VI6pG3isE0a0+BG0FSKYWVtEiVph4T67nNSDThMXKLY32/rt31kD0qFTY0sYanJdaEsGLS1mnBRVEpqC0fQuIUWtlpW0CNpjwsucimMhQzuHb05cgGwTvx/EPRdlHfR0j3DoO+DOFgFXLTB2HXIwiYrawzX1GM4hiiC2es5my8HKwKxB4t5AvEojl1ZGnU2ySAFFitVZhYpAkg3RTNJI/c63QVjfWYNWd4Wu3BvpKBpvWXOclmpokQ9U7agkThLwBPHsrKUPy8+Z3WNfixf84X59z/5vTm4HGfvGg3qBjk7MZvpHdoOT+dveKD/CVZyU5ekvgI/0Miydh472WMtNmT7QufdPvubFWK3MNODRWH+hnJGnbL1es7ojk9XRj/bHYW3vo7uRlv8bFC7nrRAAuHojrW/e2S5SQ==

Steps to reproduce

PR #11442 is not just a TS>5.1 issue; it also requires additional testing for nested Ref arrays.

What is expected?

a.value.b = 1; // OK

a.value.c[0] = 2; // OK

What is actually happening?

a.value.b = 1; // OK

a.value.c[0] = 2; // TS Error 2322

System Info

System:
    OS: macOS 13.6.7
    CPU: (16) x64 Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
    Memory: 24.11 MB / 32.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 20.16.0 - ~/.nvs/node/20.16.0/x64/bin/node
    Yarn: 1.22.22 - ~/.nvs/node/20.16.0/x64/bin/yarn
    npm: 10.8.2 - ~/.nvs/node/20.16.0/x64/bin/npm
    pnpm: 9.6.0 - ~/.nvs/node/20.16.0/x64/bin/pnpm
  Browsers:
    Chrome: 127.0.6533.90
    Safari: 17.5

Any additional comments?

https://vuejs.org/api/reactivity-core.html#ref

If an object is assigned as a ref's value, the object is made deeply reactive with reactive(). This also means if the object contains nested refs, they will be deeply unwrapped.

@jh-leong
Copy link
Member

jh-leong commented Aug 7, 2024

Thank you for reporting this issue.

FYI: this issue has existed prior to the changes merged in v3.4.35. However, I agree that it requires further investigation and testing.

@cnzgray
Copy link
Author

cnzgray commented Aug 7, 2024

Sorry, you’re right; this issue has existed in previous versions.

@cnzgray
Copy link
Author

cnzgray commented Aug 7, 2024

I just checked the type definition of UnwrapRef and found it strange. Why is it defined as UnwrapRefSimple instead of the following UnwrapRef?

      T extends ReadonlyArray<any>
            ? {
                [K in keyof T]: UnwrapRef<T[K]>;
              }

Refer to the playground below.

I hope this helps you.

@mefcorvi
Copy link
Contributor

mefcorvi commented Aug 7, 2024

It seems that types are correct here since this is actually intended behavior:

if (isRef(res)) {
// ref unwrapping - skip unwrap for Array + integer key.
return targetIsArray && isIntegerKey(key) ? res : res.value
}

You can find reasoning for this behavior here: 775a7c2

This is reflected in the documentation as well: https://vuejs.org/api/reactivity-core.html#reactive

It should also be noted that there is no ref unwrapping performed when the ref is accessed as an element of a reactive array or a native collection type like Map.

@cnzgray
Copy link
Author

cnzgray commented Aug 8, 2024

I apologize for revisiting the responsive implementation code. The current type definition is reasonable. The issue lies with TypeScript arrays; you can check this playground.

https://www.typescriptlang.org/play/?ts=5.6.0-dev.20240807#code/JYOwLgpgTgZghgYwgAgKoihANnSATANTiwFcIAeAFQBpkBlAXkoD5kBvAWAChlkBzCGGQA3YmQAUASgBcySgG5uvAM6CRYiOOGy6kxVwC+3bggD2IZULjIGyANoBdZHGXIAglChwAnuXSYcfCJSChASAFsAI2haSyhQPgAfMKjoZmZ9OAA6AAcSZQALcTZ1ENkARmQDPW5svMLi0rJZACJylqq9ZAB6buQwZXEAJgBmIaHJWrsABgcs0RCbZCHMmbmFsiWWoZb5IA

interface UnrelatedValue<T, S=T> {
  get value(): T;
  set value(v: S);
}

const a = [] as Array<UnrelatedValue<number, string|number>>;
a.push({ value: 1 });
a.push({ value: "1" }); // ts(2322)
a[0].value = 2;
a[0].value = "2";

@cnzgray
Copy link
Author

cnzgray commented Aug 8, 2024

I've discovered a new test case
In the environment of ts 5.5.4 + vue 3.4.34, the following code can correctly infer the type.
Playground for 3.4.34

import { ref } from "vue";
const g = { g: 0, h: "h" }
const e = {
  f: ref(g), 
};
const c = {
  d: ref([e]), 
};
const a = ref({
  b: ref([c]), 
});
a.value.b[0].d[0].f = g; // f type is { g: number; h: string; }

However, in the environment of ts 5.5.4 + vue 3.4.35, there is an error in type inference.
Playground for 3.4.35

a.value.b[0].d[0].f = g; // error 7053

I think the problem is related to the behavior of "Unrelated Types for Getters and Setters" under array type inference, so if TypeScript does not correctly address the issue with arrays, it might not be suitable to incorporate "Unrelated Types for Getters and Setters" into Vue.

@mefcorvi
Copy link
Contributor

mefcorvi commented Aug 8, 2024

Thank you for the test case; it's an interesting example. As far as I can tell, this was caused by the result of the ref function being declared as Ref<UnwrapRef<T>, UnwrapRef<T> | T>. After the fixes in #11536, the types in this example work correctly.

@yyx990803 yyx990803 changed the title Allow unrelated getter and setter Ref types leads to nested array type errors. Nested array refs type errors Aug 15, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants