Skip to content

Commit 21ef29b

Browse files
committed
feat: optional slots
1 parent 68f7779 commit 21ef29b

File tree

6 files changed

+41
-23
lines changed

6 files changed

+41
-23
lines changed

packages/dts-test/defineComponent.test-d.tsx

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import {
1010
SetupContext,
1111
h,
1212
SlotsType,
13-
Slots
13+
Slots,
14+
VNode
1415
} from 'vue'
1516
import { describe, expectType, IsUnion } from './utils'
1617

@@ -1409,26 +1410,37 @@ export default {
14091410
}
14101411

14111412
describe('slots', () => {
1412-
defineComponent({
1413+
const comp1 = defineComponent({
14131414
slots: Object as SlotsType<{
14141415
default: { foo: string; bar: number }
1415-
item: { data: number }
1416+
optional?: { data: string }
14161417
}>,
14171418
setup(props, { slots }) {
1418-
expectType<undefined | ((scope: { foo: string; bar: number }) => any)>(
1419+
expectType<(scope: { foo: string; bar: number }) => VNode[]>(
14191420
slots.default
14201421
)
1421-
expectType<undefined | ((scope: { data: number }) => any)>(slots.item)
1422+
expectType<((scope: { data: string }) => VNode[]) | undefined>(
1423+
slots.optional
1424+
)
1425+
1426+
slots.default({ foo: 'foo', bar: 1 })
1427+
1428+
// @ts-expect-error it's optional
1429+
slots.optional({ data: 'foo' })
1430+
slots.optional?.({ data: 'foo' })
1431+
1432+
expectType<typeof slots | undefined>(new comp1().$slots)
14221433
}
14231434
})
14241435

1425-
defineComponent({
1436+
const comp2 = defineComponent({
14261437
setup(props, { slots }) {
14271438
// unknown slots
14281439
expectType<Slots>(slots)
1429-
expectType<((...args: any[]) => any) | undefined>(slots.default)
1440+
expectType<((...args: any[]) => VNode[]) | undefined>(slots.default)
14301441
}
14311442
})
1443+
expectType<Slots | undefined>(new comp2().$slots)
14321444
})
14331445

14341446
import {

packages/dts-test/setupHelpers.test-d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,10 @@ describe('defineEmits w/ runtime declaration', () => {
184184
describe('defineSlots', () => {
185185
const slots = defineSlots<{
186186
default: { foo: string; bar: number }
187+
optional?: string
187188
}>()
188189
expectType<(scope: { foo: string; bar: number }) => VNode[]>(slots.default)
190+
expectType<undefined | ((scope: string) => VNode[])>(slots.optional)
189191

190192
const slotsUntype = defineSlots()
191193
expectType<Slots>(slotsUntype)

packages/runtime-core/src/apiSetupHelpers.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import {
2525
ExtractPropTypes
2626
} from './componentProps'
2727
import { warn } from './warning'
28-
import { Slot } from './componentSlots'
28+
import { SlotsType, TypedSlots } from './componentSlots'
2929

3030
// dev only
3131
const warnRuntimeUsage = (method: string) =>
@@ -190,11 +190,9 @@ export function defineOptions<
190190
}
191191

192192
export function defineSlots<
193-
T extends Record<string, any> = Record<string, any>
193+
S extends Record<string, any> = Record<string, any>
194194
>(): // @ts-expect-error
195-
Readonly<{
196-
[K in keyof T]: Slot<[T[K]]>
197-
}> {
195+
TypedSlots<SlotsType<S>> {
198196
if (__DEV__) {
199197
warnRuntimeUsage(`defineSlots`)
200198
}

packages/runtime-core/src/component.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
import {
3131
initSlots,
3232
InternalSlots,
33+
Slots,
3334
SlotsType,
3435
TypedSlots
3536
} from './componentSlots'
@@ -62,7 +63,8 @@ import {
6263
isPromise,
6364
ShapeFlags,
6465
extend,
65-
getGlobalThis
66+
getGlobalThis,
67+
IfAny
6668
} from '@vue/shared'
6769
import { SuspenseBoundary } from './components/Suspense'
6870
import { CompilerOptions } from '@vue/compiler-core'
@@ -125,13 +127,16 @@ export interface ComponentInternalOptions {
125127
export interface FunctionalComponent<
126128
P = {},
127129
E extends EmitsOptions = {},
128-
S extends Record<string, any> = Record<string, any>
130+
S extends Record<string, any> = any
129131
> extends ComponentInternalOptions {
130132
// use of any here is intentional so it can be a valid JSX Element constructor
131-
(props: P, ctx: Omit<SetupContext<E, SlotsType<S>>, 'expose'>): any
133+
(
134+
props: P,
135+
ctx: Omit<SetupContext<E, IfAny<S, {}, SlotsType<S>>>, 'expose'>
136+
): any
132137
props?: ComponentPropsOptions<P>
133138
emits?: E | (keyof E)[]
134-
slots?: SlotsType<S>
139+
slots?: IfAny<S, Slots, SlotsType<S>>
135140
inheritAttrs?: boolean
136141
displayName?: string
137142
compatConfig?: CompatConfig

packages/runtime-core/src/componentRenderUtils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ export function renderComponentRoot(
102102
markAttrsAccessed()
103103
return attrs
104104
},
105-
slots,
105+
slots: slots,
106106
emit
107107
}
108108
: { attrs, slots, emit }

packages/runtime-core/src/componentSlots.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ import {
1414
extend,
1515
def,
1616
SlotFlags,
17-
Prettify
17+
Prettify,
18+
IfAny
1819
} from '@vue/shared'
1920
import { warn } from './warning'
2021
import { isKeepAlive } from './components/KeepAlive'
@@ -23,8 +24,8 @@ import { isHmrUpdating } from './hmr'
2324
import { DeprecationTypes, isCompatEnabled } from './compat/compatConfig'
2425
import { toRaw } from '@vue/reactivity'
2526

26-
export type Slot<T extends any[] = any[]> = (
27-
...args: T extends [any] ? any[] : T
27+
export type Slot<T extends any = any> = (
28+
...args: IfAny<T, any[], [T]>
2829
) => VNode[]
2930

3031
export type InternalSlots = {
@@ -42,9 +43,9 @@ export type TypedSlots<S extends SlotsType> = [keyof S] extends [never]
4243
? Slots
4344
: Readonly<
4445
Prettify<{
45-
[K in keyof NonNullable<S[typeof SlotSymbol]>]:
46-
| Slot<[NonNullable<S[typeof SlotSymbol]>[K]]>
47-
| undefined
46+
[K in keyof NonNullable<S[typeof SlotSymbol]>]: Slot<
47+
NonNullable<NonNullable<S[typeof SlotSymbol]>[K]>
48+
>
4849
}>
4950
>
5051

0 commit comments

Comments
 (0)