diff --git a/packages-private/dts-test/defineComponent.test-d.tsx b/packages-private/dts-test/defineComponent.test-d.tsx index 66d323b68dd..2cc050d5ab6 100644 --- a/packages-private/dts-test/defineComponent.test-d.tsx +++ b/packages-private/dts-test/defineComponent.test-d.tsx @@ -3,7 +3,6 @@ import { type ComponentOptions, type ComponentPublicInstance, type PropType, - type Ref, type SetupContext, type Slots, type SlotsType, @@ -11,21 +10,12 @@ import { createApp, defineComponent, h, - nextTick, reactive, ref, - render as vueRender, withKeys, withModifiers, } from 'vue' import { type IsAny, type IsUnion, describe, expectType } from './utils' -import { - type TestElement, - type TestNode, - nodeOps, - serializeInner, - triggerEvent, -} from '@vue/runtime-test' describe('with object props', () => { interface ExpectedProps { @@ -2038,118 +2028,69 @@ expectString(instance.actionText) // @ts-expect-error expectString(instance.$props.actionText) -// Helper function to safely cast TestNode to TestElement -function getFirstElementChild(node: TestNode): TestElement { - if ('children' in node) { - return node.children[0] as TestElement - } - throw new Error('Expected an element with children') -} +describe('generic components in defineComponent', () => { + const GenericComp = defineComponent( + (props: { msg: string; list: T[] }) => { + return () => ( +
+ {props.msg} + {props.list.map(item => item.name).join(', ')} +
+ ) + }, + ) -// Custom render function with correct typing for TestElement -function render(vnode: any, root: TestElement) { - return vueRender(vnode, root as any) -} + const GenericCompUser = defineComponent(() => { + const list = ref([{ name: 'Tom' }, { name: 'Jack' }]) -describe('defineComponent with generics', () => { - test('defineComponent with generic', async () => { - const Comp = defineComponent({ - props: { - msg: String, - list: Array as PropType<{ name: string }[]>, - }, - setup(props) { - const count = ref(0) - const increment = () => { - count.value++ - } + return () => { + return ( +
+ msg="hello" list={list.value} /> +
+ ) + } + }) - return () => - h('div', { onClick: increment }, [ - h('h2', props.msg), - h('p', count.value), - ...(props.list || []).map((item: { name: string }) => - h('p', item.name), - ), - ]) - }, - }) + // Test correct usage + expectType() - const list: Ref<{ name: string }[]> = ref([ - { name: 'Tom' }, - { name: 'Jerry' }, - ]) + // Test GenericComp directly with correct props + expectType( + msg="hello" list={[{ name: 'Alice' }]} />, + ) - const root = nodeOps.createElement('div') - render(h(Comp, { msg: 'Hello', list: list.value }), root) - - expect(serializeInner(root)).toBe( - `

Hello

0

Tom

Jerry

`, - ) - - const firstChild = getFirstElementChild(root) - triggerEvent(firstChild, 'click') - await nextTick() - expect(serializeInner(root)).toBe( - `

Hello

1

Tom

Jerry

`, - ) - - list.value.push({ name: 'Spike' }) - await nextTick() - expect(serializeInner(root)).toBe( - `

Hello

1

Tom

Jerry

Spike

`, - ) - }) + // Test with missing required prop + expectType( + // @ts-expect-error + list={[{ name: 'Bob' }]} />, + ) - test('defineComponent with generic in render function', async () => { - const Comp = defineComponent({ - props: { - msg: String, - list: Array as PropType<{ name: string }[]>, - }, - setup(props) { - const count = ref(0) - const increment = () => { - count.value++ - } + // Test with extended type + interface Person { + name: string + age: number + } - return () => - h('div', { onClick: increment }, [ - h('h2', props.msg), - h('p', count.value), - ...(props.list || []).map((item: { name: string }) => - h('p', item.name), - ), - ]) - }, - }) + const ExtendedGenericCompUser = defineComponent(() => { + const people = ref([ + { name: 'Tom', age: 25 }, + { name: 'Jack', age: 30 }, + ]) - const list = ref([{ name: 'Tom' }, { name: 'Jerry' }]) + return () => { + return ( +
+ msg="people" list={people.value} /> +
+ ) + } + }) - const App = defineComponent({ - setup() { - return () => h(Comp, { msg: 'Hello', list: list.value }) - }, - }) + expectType() - const root = nodeOps.createElement('div') - render(h(App), root) - - expect(serializeInner(root)).toBe( - `

Hello

0

Tom

Jerry

`, - ) - - const firstChild = getFirstElementChild(root) - triggerEvent(firstChild, 'click') - await nextTick() - expect(serializeInner(root)).toBe( - `

Hello

1

Tom

Jerry

`, - ) - - list.value.push({ name: 'Spike' }) - await nextTick() - expect(serializeInner(root)).toBe( - `

Hello

1

Tom

Jerry

Spike

`, - ) - }) + // Test GenericComp directly with extended type + expectType( + msg="people" list={[{ name: 'Alice', age: 28 }]} />, + ) }) diff --git a/packages/runtime-core/src/apiDefineComponent.ts b/packages/runtime-core/src/apiDefineComponent.ts index f35c9e278f6..e7d979d5167 100644 --- a/packages/runtime-core/src/apiDefineComponent.ts +++ b/packages/runtime-core/src/apiDefineComponent.ts @@ -184,9 +184,7 @@ export type DefineComponentWithGeneric< S > & PP & { - = Generic>( - props: Props & { ref?: Ref }, - ): VNode + (props: Props & { ref?: Ref } & T): VNode } // defineComponent is a utility that is primarily used for type inference