Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

types(resolveType): fix defineComponent type error #10273

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
67 changes: 67 additions & 0 deletions packages/dts-test/defineComponent.test-d.tsx
Expand Up @@ -2,6 +2,7 @@ import {
type Component,
type ComponentOptions,
type ComponentPublicInstance,
type FunctionalContext,
type PropType,
type SetupContext,
type Slots,
Expand Down Expand Up @@ -1290,6 +1291,72 @@ describe('function syntax w/ generics', () => {
)
})

describe('function syntax w/ resolveType plugin', () => {
const Comp = defineComponent(
// jsx plugin resolveType is true
<T extends string | number>(
props: { msg: T; list: T[] },
ctx: FunctionalContext<
{
(key: 'foo', args1: string): any
(key: 'bar', args1: number): any
},
{
foo: (args1: string, args2: number) => any
default: () => any
}
>,
) => {
// use Composition API here like in <script setup>
const count = ref(0)
// @ts-expect-error
ctx.emit('foo', 1)
// @ts-expect-error
ctx.emit('bar')

ctx.emit('bar', 1)

return () => (
// return a render function (both JSX and h() works)
<div>
{props.msg} {count.value}
{// @ts-expect-error
ctx.slots.foo?.()}
{// @ts-expect-error
ctx.slots.foo?.(1)}
{ctx.slots.foo?.('', 1)}
</div>
)
},
)

expectType<JSX.Element>(<Comp msg="fse" list={['foo']} onBar={args1 => {}} />)
expectType<JSX.Element>(<Comp msg={123} list={[123]} />)

expectType<JSX.Element>(
// @ts-expect-error missing prop
<Comp msg={123} />,
)

expectType<JSX.Element>(
// @ts-expect-error generics don't match
<Comp msg="fse" list={[123]} />,
)
expectType<JSX.Element>(
// @ts-expect-error generics don't match
<Comp msg={123} list={['123']} />,
)

expectType<JSX.Element>(
// @ts-expect-error generics don't match
<Comp msg={123} list={['123']} />,
)
expectType<JSX.Element>(
// @ts-expect-error Type 'string' is not assignable to type 'number'
<Comp msg={123} list={[123]} onFoo={(arg: number) => {}} />,
)
})

describe('function syntax w/ emits', () => {
const Foo = defineComponent(
(props: { msg: string }, ctx) => {
Expand Down
24 changes: 21 additions & 3 deletions packages/runtime-core/src/apiDefineComponent.ts
Expand Up @@ -13,6 +13,7 @@ import type {
import type {
AllowedComponentProps,
ComponentCustomProps,
FunctionalContext,
SetupContext,
} from './component'
import type {
Expand All @@ -21,7 +22,11 @@ import type {
ExtractDefaultPropTypes,
ExtractPropTypes,
} from './componentProps'
import type { EmitsOptions, EmitsToProps } from './componentEmits'
import type {
EmitsOptions,
EmitsOverloadFnToProps,
EmitsToProps,
} from './componentEmits'
import { extend, isFunction } from '@vue/shared'
import type { VNodeProps } from './vnode'
import type {
Expand Down Expand Up @@ -96,6 +101,19 @@ export type DefineComponent<

// overload 1: direct setup function
// (uses user defined props interface)
export function defineComponent<
Props extends Record<string, any>,
E extends (key: any, ...args: any[]) => any,
Data extends Record<string, any>,
S extends Record<string, (...args: any[]) => any>,
Slots extends Partial<S>,
>(
setup: (
props: Props,
ctx: FunctionalContext<E, S, Data>,
) => RenderFunction | Promise<RenderFunction>,
): (props: Props & EmitsOverloadFnToProps<E>) => any

export function defineComponent<
Props extends Record<string, any>,
E extends EmitsOptions = {},
Expand All @@ -106,7 +124,7 @@ export function defineComponent<
props: Props,
ctx: SetupContext<E, S>,
) => RenderFunction | Promise<RenderFunction>,
options?: Pick<ComponentOptions, 'name' | 'inheritAttrs'> & {
options: Pick<ComponentOptions, 'name' | 'inheritAttrs'> & {
props?: (keyof Props)[]
emits?: E | EE[]
slots?: S
Expand All @@ -122,7 +140,7 @@ export function defineComponent<
props: Props,
ctx: SetupContext<E, S>,
) => RenderFunction | Promise<RenderFunction>,
options?: Pick<ComponentOptions, 'name' | 'inheritAttrs'> & {
options: Pick<ComponentOptions, 'name' | 'inheritAttrs'> & {
props?: ComponentObjectPropsOptions<Props>
emits?: E | EE[]
slots?: S
Expand Down
16 changes: 16 additions & 0 deletions packages/runtime-core/src/component.ts
Expand Up @@ -238,6 +238,22 @@ export type SetupContext<
}
: never

export type FunctionalContext<
E extends (key: any, ...args: any[]) => any = (
key: string,
...args: any[]
) => any,
S extends Record<string, (...args: any[]) => any> = {},
Data extends Record<string, any> = Record<string, any>,
> = E extends any
? {
attrs: Data
slots: Partial<S>
emit: E
expose: (exposed?: Record<string, any>) => void
}
: never

/**
* @internal
*/
Expand Down
21 changes: 21 additions & 0 deletions packages/runtime-core/src/componentEmits.ts
@@ -1,5 +1,6 @@
import {
EMPTY_OBJ,
type Prettify,
type UnionToIntersection,
camelize,
extend,
Expand Down Expand Up @@ -79,6 +80,26 @@ export type EmitFn<
}[Event]
>

type EmitsOverloadFnToPropsHelper<
T extends (key: string, ...args: any[]) => any,
PartialOverload = unknown,
P = {},
> = T extends (key: infer K, ...args: infer Args) => infer TReturn
? K extends string
? PartialOverload extends T
? P
: EmitsOverloadFnToPropsHelper<
PartialOverload & T,
PartialOverload & ((key: K, ...args: Args) => TReturn),
P & { [k in K as `on${Capitalize<K>}`]: (...args: Args) => any }
>
: never
: never

export type EmitsOverloadFnToProps<
T extends (key: any, ...args: any[]) => any,
> = Partial<Prettify<EmitsOverloadFnToPropsHelper<T>>>

export function emit(
instance: ComponentInternalInstance,
event: string,
Expand Down
1 change: 1 addition & 0 deletions packages/runtime-core/src/index.ts
Expand Up @@ -246,6 +246,7 @@ export type {
FunctionalComponent,
ComponentInternalInstance,
SetupContext,
FunctionalContext,
ComponentCustomProps,
AllowedComponentProps,
ComponentInstance,
Expand Down