Просмотр исходного кода

fix(types): support for generic keyof slots (#8374)

Carlos Rodrigues 2 лет назад
Родитель
Сommit
213eba479c

+ 96 - 0
packages/dts-test/setupHelpers.test-d.ts

@@ -260,6 +260,30 @@ describe('defineSlots', () => {
   expectType<Slots>(slotsUntype)
 })
 
+describe('defineSlots generic', <T extends Record<string, any>>() => {
+  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<boolean>({ required: true })
@@ -336,6 +360,78 @@ describe('useSlots', () => {
   expectType<Slots>(slots)
 })
 
+describe('defineSlots generic', <T extends Record<string, any>>() => {
+  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', <T extends {
+  foo: 'foo'
+  bar: 'bar'
+}>() => {
+  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<{

+ 1 - 1
packages/runtime-core/src/componentSlots.ts

@@ -44,7 +44,7 @@ export type SlotsType<T extends Record<string, any> = Record<string, any>> = {
 export type StrictUnwrapSlotsType<
   S extends SlotsType,
   T = NonNullable<S[typeof SlotSymbol]>
-> = [keyof S] extends [never] ? Slots : Readonly<T>
+> = [keyof S] extends [never] ? Slots : Readonly<T> & T
 
 export type UnwrapSlotsType<
   S extends SlotsType,