Props types checking is incorrect

Hello, I have this code inside BaseLink component:

import type { RouteLocationRaw } from 'vue-router'

interface Props {
  to: RouteLocationRaw | string
  target?: '_blank' | '_self' | '_parent' | '_top'
  isUnderlined?: boolean
}
const props = withDefaults(defineProps<Props>(), {
  isUnderlined: true,
})

My problem is that when I launch the app, I see warning in the console about invalid to prop type:
[Vue warn]: Invalid prop: type check failed for prop "to". Expected Null | String, got Object at <BaseLink to=Object { name: "loan-detail", params: {…} }>.

Do you have any idea of why this is happening? I set type for prop to as RouteLocationRaw | string, but the warning says to me that the prop expected Null | String.

Thanks for any help!

Does anyone has any idea?

Hello @microHoffman
Here the initial value is null. So, on the initial render, it throws the warning, but upon another render, it has the correct prop type (a string) and renders correctly.

For that, you have 2 options the first one is to Allow null as an option on the prop, or the second one is don’t render the component until you have the correct data.

Thx for reply. I think there is the difference between my problem and what you describe, since the thing is that the warning I am getting is not about the prop value being null, but about prop value being Object, despite that I have allowed to: RouteLocationRaw | string as a prop type…

can you provide your source code?

I think the most important part is in the first post above, but I am attaching longer edited snippet:)

// BaseLink component
<template>
  <router-link
    v-if="isLocalLink"
    :to="to"
    :class="['link', { 'link--no-underline': !isUnderlined }]"
    active-class="is-active"
    exact-active-class="is-exact-active">
    <slot/>
  </router-link>
  <a
    v-else
    :rel="isOpenedInNewTab ? 'noopener noreferrer' : false"
    :target="target"
    :href="to"
    :class="['link', { 'link--no-underline': !isUnderlined }]">
    <slot/>
  </a>
</template>

<script setup lang="ts">
import { computed, toRefs } from 'vue'
import type { RouteLocationRaw } from 'vue-router'

interface Props {
  to: RouteLocationRaw | string
  target?: '_blank' | '_self' | '_parent' | '_top'
  isUnderlined?: boolean
}

const props = withDefaults(defineProps<Props>(), {
  isUnderlined: true,
})
const { to, target, isUnderlined } = toRefs(props)

const isOpenedInNewTab = computed(() => target.value === '_blank')
const isLocalLink = computed(() => typeof to.value === 'object')
</script>

Usage like this:

// in another component
<template>
  <BaseLink
    class="loan-card"
    :is-underlined="false"
    :to="loanDetailRoute">
</template>

<script setup lang="ts">
import { RouteLocationRaw } from 'vue-router'
import RouteName from '@/router/RouteName'

interface Props {
  item: Loan | Offer
  hasLinkToDetailInHeader?: boolean
}

const props = withDefaults(defineProps<Props>(), {
  hasLinkToDetailInHeader: true,
})

const loan = computed(() => props.item instanceof Loan ? props.item : props.item.loan)
const loanDetailRoute: RouteLocationRaw = {
  name: RouteName.LoanDetail,
  params: { id: loan.value.id },
}
</script>

@microHoffman
You can try out these two way:

  1. You can replace a string with an object
interface Props {
  to: RouteLocationRaw | object
  target?: '_blank' | '_self' | '_parent' | '_top'
  isUnderlined?: boolean
}
  1. You can add both string and object
interface Props {
  to: RouteLocationRaw | string | object
  target?: '_blank' | '_self' | '_parent' | '_top'
  isUnderlined?: boolean
}

Let me know if it’s working or not.