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

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

Open
justin-schroeder opened this issue May 31, 2023 · 7 comments

Comments

@justin-schroeder
Copy link

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_
@edison1105
Copy link
Member

edison1105 commented Jun 1, 2023

should be

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

sorry, I misread.

@justin-schroeder
Copy link
Author

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

@sxzz
Copy link
Member

sxzz commented Jun 11, 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.

@justin-schroeder
Copy link
Author

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?

@iamandrewluca
Copy link

iamandrewluca commented Oct 30, 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=

@so1ve
Copy link
Member

so1ve commented Nov 4, 2023

@justin-schroeder Can you try this plz

so1ve/vue.ts@df8d0a5

@iamandrewluca
Copy link

I was able to achieve my case using this implementation.

LocalScope.vue

<script setup lang="ts" generic="T">
defineOptions({ inheritAttrs: false })
defineProps</* @vue-ignore */ T>()
</script>

<template>
  <slot v-bind="$attrs as T" />
</template>

App.vue

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

<template>
  <LocalScope item-classes="rounded p-3 bg-blue-500" #default="{ itemClasses }">
    <div :class="itemClasses">
      No Data
    </div>

    <div :class="itemClasses">
      Loading
    </div>

    <div :class="itemClasses">
      Error
    </div>

    <div :class="itemClasses">
      Data
    </div>
  </LocalScope>
</template>

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

5 participants