Subscribe on changes!

v-bind cannot be bound to a type wrapped by a Partial

avatar
Nov 20th 2023

Vue version

3.3.8

Link to minimal reproduction

https://play.vuejs.org/#eNp9kjFPwzAQhf/KyUuLVNqhWxQqQdUBBqigo5eQXIKLc7ZsJ1Sq8t85O7QUCXWJcve+l7yX+CjurZ33HYpM5L50ygbwGDoLuqDmTorgpVhJqrBWhC82KEN+egQqWsxgskMfJjDcSDohW2esz9N1NU17RQFdXZQIa0M1HCUB1MZk4INT1Ega/lLJO2IlGzLYFi6oQufRzlEYzxdjVJ54CNhaXQTkCSCvVA/97buiitPHB0gBC5byxQUnZlwsiqqZ770hbp9eGB2tVRrdT1UpsjFK1AqtzddT2gXX4ey0Lz+w/Pxnv/eHuJNi69Cj61GKsxYK12AY5c3bMx74/iy2puo001fEV/RGdzHjiD10VHHsCy6lfWyt4a9Hzc5vDgHJn0rFoJEcEi8FH4H1leq/cZfzZfLxjxDDN/ubwb0=

Steps to reproduce

View error information

What is expected?

no error

What is actually happening?

Use Partial to define the type of props and cannot be passed to the v-bind directive.

image

System Info

System:
    OS: Windows 10 10.0.19045
    CPU: (12) x64 Intel(R) Core(TM) i5-10400F CPU @ 2.90GHz
    Memory: 3.43 GB / 15.87 GB
  Binaries:
    Node: 18.1.0 - E:\code\NodeJS\node.EXE
    Yarn: 1.22.19 - E:\code\NodeJS\yarn.CMD
    npm: 8.8.0 - E:\code\NodeJS\npm.CMD
    pnpm: 8.10.5 - D:\yizhaotong-code\xueyuanjia-win\node_modules\.bin\pnpm.CMD
  Browsers:
    Edge: Spartan (44.19041.3636.0), Chromium (119.0.2151.72)
    Internet Explorer: 11.0.19041.3636
  npmPackages:
    vue: ^3.3.8 => 3.3.8

Any additional comments?

No response

avatar
Nov 23rd 2023

Try this:

<script setup lang="ts">
import { defineProps } from 'vue';

interface Conf {
  foo: string;
}

const props = defineProps(['conf']);

</script>

<template>
  <div v-bind="{ foo: conf.foo }" />
</template>

It looks like TypeScript is being a bit finicky when we try to directly bind a prop with a Partial type using the v-bind directive in Vue. Let me break it down for you.

In TypeScript, when we use Partial, we're essentially saying that all properties of T are optional. Now, when we attempt to bind such a partially defined prop using v-bind, it gets a bit confused. Vue's v-bind expects all properties to be present because it dynamically binds them.

To make TypeScript happy and ensure a smooth binding process, we have a couple of options. One is to destructure the prop and bind its properties individually. In your case, it would look like this:


<template>
  <div v-bind="{ foo: conf.foo }" />
</template>

This way, we explicitly state which properties we want to bind, and TypeScript is happy because it knows each property is there. This tells TypeScript that we are confident all properties are there, resolving any complaints. Hope this helps clarify why we're structuring the binding this way! If you have any more questions, feel free to ask.

avatar
Nov 24th 2023

@goktugerol-dev Thank you for your explanation.

1700794048355

But I'm right to write it this way. Essentially, the objects bound by v-bind are the same. But the results of type checking are different.

avatar
Nov 24th 2023

@RJQingHuan Both ways of writing the code achieve a similar outcome in terms of the actual behavior in the template. But, the difference is how TypeScript interprets and checks the types.

In v-bind="props.conf", TypeScript will check the type based on the type of props.conf. Since props.conf has the type Partial, TypeScript will understand that some properties might be missing, and it expects them to be optional. It's less explicit about which properties are being bound, so TypeScript's type checking is a bit more lenient.

In { foo: conf.foo }, you are being explicit and directly specifying which properties you want to bind in the template. TypeScript is happy with this because it sees that conf.foo is explicitly provided.

The actual behavior in the template should be similar, but the level of type checking and the strictness of TypeScript's analysis can change between the two approaches. Choose the one that aligns with the level of type safety you want in your code. It's a good practice to be aware of how TypeScript infers types,

avatar
Nov 24th 2023

@goktugerol-dev Actually not right. Vue's defineComponent function has the ability to unwrap refs, so in order to provide auto-unref ability inside <template>, Volar accesses variables from defineComponent. If you use props, Volar will access to the variable directly, so this issue will not exist. I've provided a reproduction link (see above) which is a issue in core's type.

avatar
Nov 30th 2023

I debugged this and it seems to be caused by non strict null check, please update tsconfig.json to at the very least have strictNullChecks, that should prevent typescript from resolving the type to unknown.

This PR https://github.com/vuejs/core/pull/9652 seems to fix this issue even with strictNullChecks:false.

image

strictNullChecks:false image

typescript playgroun image


EDIT: I strongly recommend using strict=true if you're using typescript, even though there's a PR that might fix it, because of the nature of this issue, we cannot maintain the fix, since we must run strictly when doing typescript tests.

I'll close this issue since as explained, we can only recommend using typescript strictly.