4

My understanding is that in Vue if you have a boolean reference named bar, in the templating syntax for v-bind you can pass :foo="bar" and Vue will dynamically unwrap the bar reference at runtime. In other words, I don't need to use :foo="bar.value".

I'm using the latest Vue and latest official Vue extension (which I believe used to be named "Volar") in VS Code. I have a Ref<string> that I got from a composable. I stored the composable in composableResult, and its composableResult.bar value is the reference.

Using the syntax :foo="composableResult.bar" syntax, VS Code underlines :foo in red and says:

Type 'ComputedRef' is not assignable to type 'boolean'. ts-plugin(2322)

I'm getting the idea that there is some TypeScript mismatch between the expected type and the reference, because TypeScript doesn't understand that Vue will dynamically unwrap the reference at runtime. But I thought the Vue extension was supposed to take care of those things.

Is it unavoidable that I'll need to use the :foo="composableResult.bar.value" syntax to avoid the error in VS Code? Or is there some setting or configuration I can change to allow me to use the :foo="composableResult.bar" syntax with no error?

Here's my relevant dependencies from package.json:

  "dependencies": {
    …
    "vue": "^3.5.13"
  },
  "devDependencies": {
    …
    "typescript": "^5.9.3",
    "vite-plugin-vue-devtools": "^8.0.2",
    "vue-tsc": "^3.1.0",
    "wxt": "^0.20.6"
  }

Here's my tsconfig.json:

{
  "extends": "./.wxt/tsconfig.json"
}

I'm using WXT. Here's my .wxt/tsconfig.json:

{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "noEmit": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "strict": true,
    "skipLibCheck": true,
    "paths": {
      "@": ["../src"],
      "@/*": ["../src/*"],
      "~": ["../src"],
      "~/*": ["../src/*"],
      "@@": [".."],
      "@@/*": ["../*"],
      "~~": [".."],
      "~~/*": ["../*"]
    }
  },
  "include": [
    "../**/*",
    "./wxt.d.ts"
  ],
  "exclude": ["../.output"]
}

My vue.volar extension version is 3.1.1.

(After feedback in my other question ref loses type information when passed from a composable in the context of a `v-model`, I updated the question to clarify I was using the result of a composable without destructuring to get the ref.)

5
  • Probably not very helpful, but I have the same setup, and am not experiencing the same issue. Commented Sep 30 at 2:00
  • Not that helpful, but good to know anyway, thanks. I added my dependency versions and TypeScript config in case anything jumps out at you. Commented Sep 30 at 13:53
  • Also a WXT project here, with similar version numbers. Main difference between your config and mine is that I don't use vite-plugin-vue-devtools. Commented Oct 1 at 1:11
  • There is a newer major version of vue-tsc available - try updating it with yarn add --dev vue-tsc. Commented Oct 4 at 17:20
  • This is just a guess, but you might have outdated packages that are 'officially' valid dependency versions, but cause issues in specific combinations. When such packages are only transitively listed and not directly in your package.json, npm may decide not to update them even when you update your actual dependencies. I've had this cause various issues including bad vue-tsc results, and had to delete both the package-lock and node_modules then do an install to force npm to build a brand new dependency tree. Commented Oct 4 at 22:40

1 Answer 1

1
+50

This is the same problem as described in this answer. This is the problem with the actual code, it's revealed as TypeScript type error because VS Code's Volar provides type checking for Vue templates.

The problem is that only top-level refs are automatically unwrapped in templates, so this v-model:foo="composableResult.bar" is expected to cause type error, as composableResult.bar is ref object rather than a value.

If composableResult is the result of some composable then bar should destructured from result object in order to be top-level ref in in script setup:

const { testFoo } = useComposable();

While script doesn't restrict a developer in a way the variables are declared, but it should be exposed to a template as top-level ref any way:

const composableResult = useComposable();
...
return { testFoo: composableResult.testFoo }

And if composableResult is one inseparable reactive object that isn't supposed to be destructured when a composable is called, it could be defined as such, so a ref is instantly unwrapped, and testFoo value is uniformly available as composableResult.testFoo in script and template blocks:

function composableResult() {
  ...
  return reactive({ testFoo });
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you immensely for clearing this up!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.