Skip to content

JSX compatible slots #2472

@KaelWD

Description

@KaelWD

I swear this used to work ages ago.

Vuetify defines slots as $props.$children and sets JSX.ElementChildrenAttribute to $children. Vue already comes with JSX.ElementAttributesProperty as $props so this works great in JSX:

// import { VMenu } from 'vuetify/components'
import type { DefineComponent, VNodeChild } from 'vue'

declare const VMenu: DefineComponent<{
  $children: {
    activator: (props: { isActive: boolean }) => VNodeChild
  }
}>

const el = (
  <VMenu>
    {{ activator: props => String(props.isActive) }}
  </VMenu>
)

Volar is only looking at $slots or children though, so we'd have to define our slot types twice for them to work with it.

export type ExtractComponentSlots<T> =
IsAny<T> extends true ? Record<string, any>
: T extends { ${slots}?: infer S } ? { [K in keyof S]-?: S[K] extends ((obj: infer O) => any) | undefined ? O : any }
: T extends { children?: infer S } ? { [K in keyof S]-?: S[K] extends ((obj: infer O) => any) | undefined ? O : any }
: Record<string, any>;

This also means that slots from .vue files only work in other .vue files and can't be used in JSX:

<script setup lang="ts"></script>

<template>
  <slot name="activator" :isActive="false" />
</template>
import Slots from './Slots.vue'

const el = (
  <Slots>
    {{ foo: props => props.isActive }} // expected boolean, got implicit any
  </Slots>
)

vuejs/core#7083 adds ElementChildrenAttribute to vue core.

Volar could check T[keyof JSX.ElementAttributesProperty][keyof JSX.ElementChildrenAttribute] to support any combination of these, and generate its own slots in $props somewhere so they can be used in JSX.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions