From 213eba479ce080efc1053fe636f6be4a4c889b44 Mon Sep 17 00:00:00 2001 From: Carlos Rodrigues Date: Fri, 8 Dec 2023 14:54:57 +0000 Subject: [PATCH] fix(types): support for generic keyof slots (#8374) --- packages/dts-test/setupHelpers.test-d.ts | 96 +++++++++++++++++++++ packages/runtime-core/src/componentSlots.ts | 2 +- 2 files changed, 97 insertions(+), 1 deletion(-) diff --git a/packages/dts-test/setupHelpers.test-d.ts b/packages/dts-test/setupHelpers.test-d.ts index 51f95c00944..53c4d859788 100644 --- a/packages/dts-test/setupHelpers.test-d.ts +++ b/packages/dts-test/setupHelpers.test-d.ts @@ -260,6 +260,30 @@ describe('defineSlots', () => { expectType(slotsUntype) }) +describe('defineSlots generic', >() => { + const props = defineProps<{ + item: T + }>() + + const slots = defineSlots< + { + [K in keyof T as `slot-${K & string}`]?: (props: { item: T }) => any + } & { + label?: (props: { item: T }) => any + } + >() + + for (const key of Object.keys(props.item) as (keyof T & string)[]) { + slots[`slot-${String(key)}`]?.({ + item: props.item + }) + } + slots.label?.({ item: props.item }) + + // @ts-expect-error calling wrong slot + slots.foo({}) +}) + describe('defineModel', () => { // overload 1 const modelValueRequired = defineModel({ required: true }) @@ -336,6 +360,78 @@ describe('useSlots', () => { expectType(slots) }) +describe('defineSlots generic', >() => { + const props = defineProps<{ + item: T + }>() + + const slots = defineSlots< + { + [K in keyof T as `slot-${K & string}`]?: (props: { item: T }) => any + } & { + label?: (props: { item: T }) => any + } + >() + + // @ts-expect-error slots should be readonly + slots.label = () => {} + + // @ts-expect-error non existing slot + slots['foo-asdas']?.({ + item: props.item + }) + for (const key in props.item) { + slots[`slot-${String(key)}`]?.({ + item: props.item + }) + slots[`slot-${String(key as keyof T)}`]?.({ + item: props.item + }) + } + + for (const key of Object.keys(props.item) as (keyof T)[]) { + slots[`slot-${String(key)}`]?.({ + item: props.item + }) + } + slots.label?.({ item: props.item }) + + // @ts-expect-error calling wrong slot + slots.foo({}) +}) + +describe('defineSlots generic strict', () => { + const props = defineProps<{ + item: T + }>() + + const slots = defineSlots< + { + [K in keyof T as `slot-${K & string}`]?: (props: { item: T }) => any + } & { + label?: (props: { item: T }) => any + } + >() + + // slot-bar/foo should be automatically inferred + slots['slot-bar']?.({ item: props.item }) + slots['slot-foo']?.({ item: props.item }) + + slots.label?.({ item: props.item }) + + // @ts-expect-error not part of the extends + slots['slot-RANDOM']?.({ item: props.item }) + + // @ts-expect-error slots should be readonly + slots.label = () => {} + + // @ts-expect-error calling wrong slot + slots.foo({}) +}) + // #6420 describe('toRefs w/ type declaration', () => { const props = defineProps<{ diff --git a/packages/runtime-core/src/componentSlots.ts b/packages/runtime-core/src/componentSlots.ts index 980ee799186..99936f49dec 100644 --- a/packages/runtime-core/src/componentSlots.ts +++ b/packages/runtime-core/src/componentSlots.ts @@ -44,7 +44,7 @@ export type SlotsType = Record> = { export type StrictUnwrapSlotsType< S extends SlotsType, T = NonNullable -> = [keyof S] extends [never] ? Slots : Readonly +> = [keyof S] extends [never] ? Slots : Readonly & T export type UnwrapSlotsType< S extends SlotsType,