Subscribe on changes!

@vue/compiler-sfc cannot build <script setup> components that use generic discriminated union props

avatar
May 31st 2023

Vue version

3.3.4

Link to minimal reproduction

https://github.com/justin-schroeder/generics-discriminated-union-reproduction

Steps to reproduce

Any component with generics, where the generics extend a discriminated union cannot be compiled by @vue/compiler-sfc — however, they work just fine with Volar.

// Input.vue
<script setup lang="ts" generic="P extends Inputs">
import type { Inputs } from '../props.ts'

defineProps<P>()
</script>
// props.ts
type Text = { type: 'text', value: string }
type Number = { type: 'number', value: number }

export type Inputs = Text | Number

What is expected?

Typed prop unions work both in both Volar and build time.

What is actually happening?

The following error is thrown:

[vite] Internal server error: [@vue/compiler-sfc] Unresolvable type reference or unsupported built-in utility type

/src/components/Input.vue
2  |  import type { Inputs } from '../props.ts'
3  |  
4  |  const props = defineProps<P>()
   |                            ^
5  |  </script>

Screenshot 2023-05-31 at 1 46 37 PM

Full reproduction repository here: https://github.com/justin-schroeder/generics-discriminated-union-reproduction

System Info

System:
    OS: macOS 13.1
    CPU: (10) arm64 Apple M1 Pro
    Memory: 63.72 MB / 16.00 GB
    Shell: 5.8.1 - /bin/zsh
  Binaries:
    Node: 18.14.2 - /usr/local/bin/node
    Yarn: 1.22.18 - ~/.yarn/bin/yarn
    npm: 9.5.0 - /usr/local/bin/npm
  Browsers:
    Brave Browser: 113.1.51.118
    Chrome: 113.0.5672.126
    Edge: 113.0.1774.57
    Firefox: 111.0.1
    Safari: 16.2
  npmPackages:
    vue: 3.3.4 => 3.3.4


### Any additional comments?

_No response_
avatar
Jun 1st 2023

should be

// props.ts
type Text = { text: string }
type Number = { number: number }

sorry, I misread.

avatar
Jun 1st 2023

🤔 no, that's not the intended props. I did in fact mean the discriminated union that was provided.

avatar
Jun 11th 2023

Currently, Vue disallows dynamic props keys that from TS types. Because Vue's compiler needs to analyze TS types and transform them into the runtime definition of a specific prop, and it cannot be analyzed statically in build time.

avatar
Jun 12th 2023

Ok, that actually makes a lot of sense @sxzz.

Related: Has any consideration been giving to providing a mechanism to remove the distinction of props/attrs and treating all of them the same (as props)? This would be useful for library authors who would like to create dynamic prop APIs. To prevent breaking changes this could be an opt-in option on a per-component basis, similar to inheritAttrs?

avatar
Oct 30th 2023

Hey there! Another use case. My example is different, but the error message seems the same (not related to discriminated unions).

There are compiler errors, but Volar seems to work fine.

Error: [@vue/compiler-sfc] Unresolvable type reference or unsupported built-in utility type

src/LocalScope.vue
4  |  
5  |  <script setup lang="ts" generic="TProps extends Record<string, unknown>">
6  |  const props = defineProps<TProps>()
   |                            ^^^^^^

Code

<template>
  <slot v-bind="props" />
</template>

<script setup lang="ts" generic="TProps extends Record<string, unknown>">
const props = defineProps<TProps>()

defineSlots<{
  default(props: TProps): JSX.Element
}>()
</script>

Usage

<script setup>
import LocalScope from './LocalScope.vue'
</script>

<template>
  <LocalScope v-slot="{ test, lorem, ipsum }" :lorem="42" ipsum="dolor">
                     // ^ expected TS error
    <span>{{ test }}</span>
    <span>{{ lorem }}</span>
    <span>{{ ipsum }}</span>
  </LocalScope>
</template>

https://play.vuejs.org/#eNp9U01v2zAM/SuELm0BzwHWnQw3wDbksGLYiqaHHXTxZMZTK0uCRKUBAv/3UfKaOsPWm/keHz/E56P46H29Tyga0UYVtCeISMmvpdWjd4Hgq1Od2SrnEXbBjXBRr16hLL2Qtl3NWlZxQDh60xFyBNAu9Pt30Ti6keIIhJEqMC7gWIH2MY0wSQFNQTjjw3uOCs5B7xiWotTjitF3dn2ca8A0cfMM/EWWQv9l/3Rcsu1iLQba1WkNUQmKytmdHurH6Cy/1TErpFBu9Npg+O5JOxulaKAwmeuMcc+3BaOQsHrB1S9UT//AH+MhY1LcBYwY9ijFiaMuDEgzvdl+wwN/n8jR9clw9hvkPUZnUp5xTvuUbM9jL/LKtF/KxbUdHuLmQGjjy1J50Jw5lXwp+Oqf31j9ddzr+rropJ34Fc99ky13bpXsDjbJT217vroPznNdWJ1fo1hsaVUwnR04n3LygBaDVhw+3GU98HOg7SPco3KhbyMF3q+CZJ+se+bLZ1fxbdlJpR/cQI87bbGo27nI+vIqd52JLQ8Z27IrA10ydFmUDczJVw3cbn/UG4MjWuLFi/r0h4jpN/g9LEw=