Skip to content

Volar: v-model type not narrowed with discriminated union props in SFC (Vue 3.5, TS 5.8) #5649

@50Bytes-dev

Description

@50Bytes-dev

Volar does not narrow v-model type with discriminated union props in SFC

Summary

Volar/Language Tools fails to narrow v-model type in <script setup> when component props use a discriminated union. The code runs, but IDE type analysis reports incorrect component/slot types and v-model type is not narrowed based on the discriminator.

Expected

  • With a discriminated union on props (e.g., onlyId: true vs onlyId?: false), IDE should narrow modelValue type accordingly, and useVModel/v-model should reflect that type in both template and parent usage.

Actual

  • Narrowing is lost: IDE treats modelValue / v-model as the wide union and misreports types in template/slots. Runtime is fine.

Minimal Reproduction (SFC)

<script setup lang="ts">
import { useVModel } from "@vueuse/core"
import type { Ref } from "vue"

type Client = { id: number; full_name: string }

type BaseProps = {
  label?: string
}

type PropsWithOnlyId = BaseProps & {
  onlyId: true
  modelValue?: number | null
}

type PropsWithoutOnlyId = BaseProps & {
  onlyId?: false
  modelValue?: Client | number | null
}

type ComponentProps = PropsWithOnlyId | PropsWithoutOnlyId

const props = defineProps<ComponentProps>()
const emit = defineEmits<{ "update:modelValue": [value: Client | number | null] }>()

// Inference-friendly usage (no generics):
const model = useVModel(props, "modelValue", emit)

// Expected: if props.onlyId === true => model: Ref<number | null>
// Actual: IDE treats model as Ref<Client | number | null> (no narrowing)
</script>
<template>
  <input v-model="model" />
  <!-- Expected: narrowed to number|null when onlyId is true -->
</template>

Notes

  • If we remove the discriminated union (single props type), IDE behaves but we lose correctness.
  • If we force generics on useVModel, we hit generic constraints differences across VueUse versions and it regresses in IDE. The most stable is relying on inference, but narrowing still fails.

Versions

  • Vue: 3.5.18
  • TypeScript: 5.8.3
  • vue-tsc: 3.0.1
  • @vueuse/core: 13.4.0
  • Node: 22.17.0
  • Volar / language-tools: latest (observed with VS Code + Vue extension)

Repository / Context

  • Real-world component where it reproduces (simplified): frontend/apps/company/src/entities/client/ui/client-select/ClientSelect.vue

What would help

  • Either improved narrowing for discriminated unions in defineProps + useVModel paths, or guidance/workaround to keep IDE narrowing without sacrificing runtime ergonomics.

Related

Thanks!

Metadata

Metadata

Assignees

No one assigned

    Labels

    questionFurther information is requested

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions