Skip to content

Prop type inference for typeof Symbol() is incorrect and breaks Vue runtime checks #12234

Open
@JoCa96

Description

@JoCa96

Vue version

3.5.12

Link to minimal reproduction

https://play.vuejs.org/#eNp9UsluwjAQ/RXLJyqhoJb2AoGqC4dWaosKR0uVCRMIdWzLdlhE8+8dO5Ag1HKyZ96bmTfLnj5oHa0LoD0a28Rk2hELrtBDJrNcK+PIk8o1SY3KCaNRx1uez2ifybhThSAZDQe5FtwBWoTEIayD/7hTA7RNnU2UTLNFtLJKYtG9JzOaIDsTYD60y5S0jPZIQDzGhVCb1+BzpoD20Z8sIfn+w7+yW+9jdGzAglmj1hpz3CzAVfBo8g5b/NdgruaFQPYF8BOsEoXXWNEeCzlH2Se8oPYljC6Ti6kdbR1Ie2zKC/XMMvAZxUn6Qf3XeiO3G92GOCZLnOJxC+dbI4LLxQDjME2zwT152012+UyJNqne6U4DKZut8rBOJjeZWz5DygvhbGsOaSZhbJS2cVC05qKA+95pjh8yU0oAlxheDltXbSwWaL26JimvLp+KATkHY88vhUfOYn+wDT3g1VjX5BwcRLR87gPFeUEn2gbBo9I6qo9Zv9ZYCueMmbvRXXR9Q8tfnZgErg==

Steps to reproduce

  1. Export a symbol in a typescript file a.ts
// a.ts
export const MySmbol = Symbol();
  1. In a Vue file Comp.vue with <script setup lang="ts"> import and use the symbol as prop type and default:
  2. Update the prop type to be a union of the symbol type and some other inferable type, e.g., boolean.
  3. Use the symbol as default value:
// Comp.vue
<script setup lang="ts">
import { MySmbol } from "./a";

withDefaults(defineProps<{
  value?: typeof MySmbol | boolean;
}>(), { value: MySymbol });
</script>

<template>
  renders
</template>

==> Check the JS output, and you can see that only the boolean type of the type union is inferred:

 props: {
    value: { type: [Boolean], default: MySymbol }
  },
  1. Switch to Prod mode
  2. Create a second component, import and use the first component:
// App.vue
<script setup>
import Comp from "./Comp.vue";
</script>

<template>
  <Comp />
</template>
  1. Switch to DEV mode.
    ==> Now you will get the error: can't convert symbol to string

Why is the DEV mode switching relevant?
We are building a component library and there we use symbols as explicit defaults for special default behaviors.
This issue was only encountered when a user tried to develop (in dev mode) using the compiled lib (in prod mode).

See also: SchwarzIT/onyx#1980 (comment)

What is expected?

  • The symbol type should be inferred correctly or no runtime check at all should be happening:
 props: {
    value: { type: [Symbol, Boolean], default: MySymbol }
  },

or

props: {
    value: { default: SYMBOL }
  },
  • No error when a (PROD) compiled component is used for development

What is actually happening?

Vue's compiler inferred the prop type used for runtime validation as Boolean, even though the typescript type is typeof unique symbol | boolean. This causes the runtime validation to fail and trying to log an error. Because Vue tries to create an error message using implicit string conversions and symbols cannot be implicitly converted to a string, the JS runtime throws: Uncaught (in promise) TypeError: can't convert symbol to string

System Info

System:
    OS: macOS 13.6.7
    CPU: (10) arm64 Apple M1 Max
    Memory: 81.38 MB / 32.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 20.17.0 - ~/Library/Caches/fnm_multishells/37685_1729492712079/bin/node
    Yarn: 3.4.1 - ~/Library/Caches/fnm_multishells/37685_1729492712079/bin/yarn
    npm: 10.8.2 - ~/Library/Caches/fnm_multishells/37685_1729492712079/bin/npm
    pnpm: 9.7.1 - ~/Library/Caches/fnm_multishells/37685_1729492712079/bin/pnpm
    bun: 1.1.21 - ~/.bun/bin/bun
  Browsers:
    Brave Browser: 122.1.63.169
    Edge: 130.0.2849.46
    Safari: 17.5

Any additional comments?

We try to use symbols as explicit defaults in place of undefined, because the implicit default for boolean is false.

You can find playground examples with different usages of unique symbols here:
https://play.vuejs.org/#__PROD__eNp9Ul1LwzAU/SuXPCmMil8vc06c+KA4J24gQkBqezuraRKSdHbM/Xdv0nbWKXtqcs+5t+eenBW71DpalMj6bGATk2sHFl2pQcRyfs6Zs5wNucwLrYyDFUyfx6PJXQ+my+JViQejdHuemJFSAmPZgycTa40G1pAZVQBn0YEmJo0645LLFLNcou+1gxWXAPV0TGdLjZOsHnfR7/yD2gCUFMsthqOryhpRgVTkVTtnG4cveK0VBmb70/Gvjq1dAvMzrJMS2iw2aOYMCV4P9/bpMzio3SOv6OKw0CJ2SDcAg7SxsZ7TqbMeeZsomeXz6N0qSQ8QvOAsUYXOBZqJdrmSZFofAuKxWAj1eRtqzpTYa+vJGyYf/9TfbeVrnD0YtGgWyNkGc7GZo6vh6+k9VnTegIVKS0HsHeAjWiVKr7GmjUqZkuwOL6i9CT7ncj6z15VDadulvFDPXAc+Z5TCqx2r/8g9jk5CH5lPLoZkRc6SgViFlJKr1rWPft486R5n42V95My/WEP2EekkjfjbofpL3MRjMz20dvPVbWpTMxsSf/aLRvpfFhQO2pb0H0en0eERW38DPKE6Rw==

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions