diff --git a/packages-private/dts-test/defineComponent.test-d.tsx b/packages-private/dts-test/defineComponent.test-d.tsx index 79ce6d6956d..9e1c445493f 100644 --- a/packages-private/dts-test/defineComponent.test-d.tsx +++ b/packages-private/dts-test/defineComponent.test-d.tsx @@ -2027,3 +2027,35 @@ expectString(instance.actionText) // public prop on $props should be optional // @ts-expect-error expectString(instance.$props.actionText) + +describe('with Slot', () => { + defineComponent({ + slots: Object as SlotsType<{ + default: { foo: string; bar: number } + optional?: { data: string } + }>, + setup(props, { slots }) { + expectType< + (scope: { + foo: string + bar: number + }) => VNode[] | VNode | null | undefined + >(slots.default) + expectType< + | ((scope: { data: string }) => VNode[] | VNode | null | undefined) + | undefined + >(slots.optional) + + // Test slot invocation + const defaultResult = slots.default({ foo: 'foo', bar: 1 }) + expectType(defaultResult) + + const optionalResult = slots.optional?.({ data: 'foo' }) + expectType(optionalResult) + + // Test that single VNode, null, and undefined are allowed + slots.default({ foo: 'foo', bar: 1 }) + slots.optional?.({ data: 'foo' }) + }, + }) +}) diff --git a/packages/runtime-core/src/componentSlots.ts b/packages/runtime-core/src/componentSlots.ts index 8f8392f1cdb..375583947bf 100644 --- a/packages/runtime-core/src/componentSlots.ts +++ b/packages/runtime-core/src/componentSlots.ts @@ -27,6 +27,10 @@ export type Slot = ( ...args: IfAny ) => VNode[] +export type FlexibleSlot = ( + ...args: IfAny +) => VNode[] | VNode | null | undefined + export type InternalSlots = { [name: string]: Slot | undefined } @@ -84,14 +88,14 @@ const normalizeSlotValue = (value: unknown): VNode[] => ? value.map(normalizeVNode) : [normalizeVNode(value as VNodeChild)] -const normalizeSlot = ( +const normalizeSlot = ( key: string, - rawSlot: Function, + rawSlot: FlexibleSlot, ctx: ComponentInternalInstance | null | undefined, ): Slot => { if ((rawSlot as any)._n) { // already normalized - #5353 - return rawSlot as Slot + return rawSlot as Slot } const normalized = withCtx((...args: any[]) => { if ( @@ -105,8 +109,12 @@ const normalizeSlot = ( `Invoke the slot function inside the render function instead.`, ) } - return normalizeSlotValue(rawSlot(...args)) - }, ctx) as Slot + return normalizeSlotValue( + rawSlot( + ...(args as IfAny), + ), + ) + }, ctx) as Slot // NOT a compiled slot ;(normalized as ContextualRenderFn)._c = false return normalized @@ -122,7 +130,7 @@ const normalizeObjectSlots = ( if (isInternalKey(key)) continue const value = rawSlots[key] if (isFunction(value)) { - slots[key] = normalizeSlot(key, value, ctx) + slots[key] = normalizeSlot(key, value as Slot, ctx) } else if (value != null) { if ( __DEV__ &&