diff --git a/packages/nuxt/src/app/components/nuxt-link.ts b/packages/nuxt/src/app/components/nuxt-link.ts index 309fce5227..a3a07573c7 100644 --- a/packages/nuxt/src/app/components/nuxt-link.ts +++ b/packages/nuxt/src/app/components/nuxt-link.ts @@ -2,17 +2,21 @@ import type { AllowedComponentProps, AnchorHTMLAttributes, ComputedRef, - DefineComponent, - InjectionKey, PropType, + DefineSetupFnComponent, + InjectionKey, + PropType, + SlotsType, + UnwrapRef, + VNode, VNodeProps, } from 'vue' import { computed, defineComponent, h, inject, onBeforeUnmount, onMounted, provide, ref, resolveComponent } from 'vue' -import type { RouteLocation, RouteLocationRaw, Router, RouterLink, RouterLinkProps, useLink } from 'vue-router' +import type { RouteLocation, RouteLocationRaw, Router, RouterLink, RouterLinkProps, UseLinkReturn, useLink } from 'vue-router' import { hasProtocol, joinURL, parseQuery, withTrailingSlash, withoutTrailingSlash } from 'ufo' import { preloadRouteComponents } from '../composables/preload' import { onNuxtReady } from '../composables/ready' import { navigateTo, resolveRouteObject, useRouter } from '../composables/router' -import { useNuxtApp, useRuntimeConfig } from '../nuxt' +import { type NuxtApp, useNuxtApp, useRuntimeConfig } from '../nuxt' import { cancelIdleCallback, requestIdleCallback } from '../compat/idle-callback' // @ts-expect-error virtual file @@ -28,7 +32,8 @@ const NuxtLinkDevKeySymbol: InjectionKey = Symbol('nuxt-link-dev-key') * `` is a drop-in replacement for both Vue Router's `` component and HTML's `` tag. * @see https://nuxt.com/docs/api/components/nuxt-link */ -export interface NuxtLinkProps extends Omit { +export interface NuxtLinkProps extends Omit { + custom?: CustomProp /** * Route Location the link should navigate to when clicked on. */ @@ -102,6 +107,24 @@ export interface NuxtLinkOptions extends prefetchOn?: Exclude } +type NuxtLinkDefaultSlotProps = CustomProp extends true + ? { + href: string + navigate: () => Promise + prefetch: (nuxtApp?: NuxtApp) => Promise + route: (RouteLocation & { href: string }) | undefined + rel: string | null + target: '_blank' | '_parent' | '_self' | '_top' | (string & {}) | null + isExternal: boolean + isActive: false + isExactActive: false + } + : UnwrapRef + +type NuxtLinkSlots = { + default?: (props: NuxtLinkDefaultSlotProps) => VNode[] +} + /* @__NO_SIDE_EFFECTS__ */ export function defineNuxtLink (options: NuxtLinkOptions) { const componentName = options.componentName || 'NuxtLink' @@ -466,14 +489,19 @@ export function defineNuxtLink (options: NuxtLinkOptions) { isExternal: isExternal.value || hasTarget.value, isActive: false, isExactActive: false, - }) + } satisfies NuxtLinkDefaultSlotProps) } // converts `""` to `null` to prevent the attribute from being added as empty (`href=""`) return h('a', { ref: el, href: href.value || null, rel, target }, slots.default?.()) } }, - }) as unknown as DefineComponent + // }) as unknown as DefineComponent> + }) as unknown as new(props: NuxtLinkProps) => InstanceType, + [], + SlotsType> + >> } export default defineNuxtLink(nuxtLinkDefaults) diff --git a/packages/nuxt/test/nuxt-link.test.ts b/packages/nuxt/test/nuxt-link.test.ts index 057214f3a1..878f8e2379 100644 --- a/packages/nuxt/test/nuxt-link.test.ts +++ b/packages/nuxt/test/nuxt-link.test.ts @@ -55,8 +55,9 @@ const nuxtLink = ( ): { type: string, props: Record, slots: unknown } => { const component = defineNuxtLink({ componentName: 'NuxtLink', ...nuxtLinkOptions }) - const [type, _props, slots] = (component.setup as unknown as (props: NuxtLinkProps, context: { slots: Record unknown> }) => - () => [string, Record, unknown])(props, { slots: { default: () => null } })() + const [type, _props, slots] = ( + component as unknown as { setup: (props: NuxtLinkProps, context: { slots: Record unknown> }) => () => [string, Record, unknown] } + ).setup(props, { slots: { default: () => null } })() return { type, props: _props, slots } }