Skip to content

feat: add component $emit typing support #846

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

Merged
merged 2 commits into from
Nov 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/apis/createApp.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type Vue from 'vue'
import { VueConstructor } from 'vue/types/umd'
import { VueConstructor } from 'vue'
import { getVueConstructor } from '../runtimeContext'
import { warn } from '../utils'

Expand Down
4 changes: 1 addition & 3 deletions src/apis/createElement.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import Vue from 'vue'
import type { CreateElement } from 'vue'
import { getVueConstructor, getCurrentInstance } from '../runtimeContext'
import { defineComponentInstance } from '../utils/helper'
import { warn } from '../utils'

type CreateElement = Vue['$createElement']

let fallbackCreateElement: CreateElement

export const createElement = function createElement(...args: any) {
Expand Down
43 changes: 27 additions & 16 deletions src/component/componentOptions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Vue, { VNode, ComponentOptions as Vue2ComponentOptions } from 'vue'
import { SetupContext } from '../runtimeContext'
import { EmitsOptions, SetupContext } from '../runtimeContext'
import { Data } from './common'
import { ComponentPropsOptions, ExtractPropTypes } from './componentProps'
import { ComponentRenderProxy } from './componentProxy'
Expand All @@ -8,13 +8,6 @@ export { ComponentPropsOptions } from './componentProps'
export type ComputedGetter<T> = (ctx?: any) => T
export type ComputedSetter<T> = (v: T) => void

export type ObjectEmitsOptions = Record<
string,
((...args: any[]) => any) | null
>

export type EmitsOptions = ObjectEmitsOptions | string[]

export interface WritableComputedOptions<T> {
get: ComputedGetter<T>
set: ComputedSetter<T>
Expand All @@ -39,7 +32,10 @@ interface ComponentOptionsBase<
Props,
D = Data,
C extends ComputedOptions = {},
M extends MethodOptions = {}
M extends MethodOptions = {},
Mixin = {},
Extends = {},
Emits extends EmitsOptions = {}
> extends Omit<
Vue2ComponentOptions<Vue, D, M, C, Props>,
'data' | 'computed' | 'method' | 'setup' | 'props'
Expand Down Expand Up @@ -67,37 +63,52 @@ export type ComponentOptionsWithProps<
D = Data,
C extends ComputedOptions = {},
M extends MethodOptions = {},
Mixin = {},
Extends = {},
Emits extends EmitsOptions = {},
Props = ExtractPropTypes<PropsOptions>
> = ComponentOptionsBase<Props, D, C, M> & {
props?: PropsOptions
emits?: (EmitsOptions | string[]) & ThisType<void>
emits?: Emits & ThisType<void>
setup?: SetupFunction<Props, RawBindings>
} & ThisType<ComponentRenderProxy<Props, RawBindings, D, C, M>>
} & ThisType<
ComponentRenderProxy<Props, RawBindings, D, C, M, Mixin, Extends, Emits>
>

export type ComponentOptionsWithArrayProps<
PropNames extends string = string,
RawBindings = Data,
D = Data,
C extends ComputedOptions = {},
M extends MethodOptions = {},
Mixin = {},
Extends = {},
Emits extends EmitsOptions = {},
Props = Readonly<{ [key in PropNames]?: any }>
> = ComponentOptionsBase<Props, D, C, M> & {
props?: PropNames[]
emits?: (EmitsOptions | string[]) & ThisType<void>
emits?: Emits & ThisType<void>
setup?: SetupFunction<Props, RawBindings>
} & ThisType<ComponentRenderProxy<Props, RawBindings, D, C, M>>
} & ThisType<
ComponentRenderProxy<Props, RawBindings, D, C, M, Mixin, Extends, Emits>
>

export type ComponentOptionsWithoutProps<
Props = {},
RawBindings = Data,
D = Data,
C extends ComputedOptions = {},
M extends MethodOptions = {}
M extends MethodOptions = {},
Mixin = {},
Extends = {},
Emits extends EmitsOptions = {}
> = ComponentOptionsBase<Props, D, C, M> & {
props?: undefined
emits?: (EmitsOptions | string[]) & ThisType<void>
emits?: Emits & ThisType<void>
setup?: SetupFunction<Props, RawBindings>
} & ThisType<ComponentRenderProxy<Props, RawBindings, D, C, M>>
} & ThisType<
ComponentRenderProxy<Props, RawBindings, D, C, M, Mixin, Extends, Emits>
>

export type WithLegacyAPI<T, D, C, M, Props> = T &
Omit<Vue2ComponentOptions<Vue, D, M, C, Props>, keyof T>
58 changes: 51 additions & 7 deletions src/component/componentProxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,29 @@ import {
ComponentInternalInstance,
EmitFn,
EmitsOptions,
ObjectEmitsOptions,
Slots,
} from '../runtimeContext'

type EmitsToProps<T extends EmitsOptions> = T extends string[]
? {
[K in string & `on${Capitalize<T[number]>}`]?: (...args: any[]) => any
}
: T extends ObjectEmitsOptions
? {
[K in string &
`on${Capitalize<string & keyof T>}`]?: K extends `on${infer C}`
? T[Uncapitalize<C>] extends null
? (...args: any[]) => any
: (
...args: T[Uncapitalize<C>] extends (...args: infer P) => any
? P
: never
) => any
: never
}
: {}

export type ComponentInstance = InstanceType<VueConstructor>

// public properties exposed on the proxy, which is used as the render context
Expand All @@ -34,6 +54,9 @@ export type ComponentRenderProxy<
D = {}, // return from data()
C extends ComputedOptions = {},
M extends MethodOptions = {},
Mixin = {},
Extends = {},
Emits extends EmitsOptions = {},
PublicProps = P,
Defaults = {},
MakeDefaultsOptional extends boolean = false
Expand All @@ -45,28 +68,37 @@ export type ComponentRenderProxy<
: P & PublicProps
>
$attrs: Data
$emit: EmitFn<Emits>
} & Readonly<P> &
ShallowUnwrapRef<B> &
D &
M &
ExtractComputedReturns<C> &
Omit<Vue, '$data' | '$props' | '$attrs'>
Omit<Vue, '$data' | '$props' | '$attrs' | '$emit'>

// for Vetur and TSX support
type VueConstructorProxy<
PropsOptions,
RawBindings,
Data,
Computed extends ComputedOptions,
Methods extends MethodOptions
> = VueConstructor & {
Methods extends MethodOptions,
Mixin = {},
Extends = {},
Emits extends EmitsOptions = {},
Props = ExtractPropTypes<PropsOptions> &
({} extends Emits ? {} : EmitsToProps<Emits>)
> = Omit<VueConstructor, never> & {
new (...args: any[]): ComponentRenderProxy<
ExtractPropTypes<PropsOptions>,
Props,
ShallowUnwrapRef<RawBindings>,
Data,
Computed,
Methods,
ExtractPropTypes<PropsOptions>,
Mixin,
Extends,
Emits,
Props,
ExtractDefaultPropTypes<PropsOptions>,
true
>
Expand All @@ -81,7 +113,10 @@ export type VueProxy<
RawBindings,
Data = DefaultData<Vue>,
Computed extends ComputedOptions = DefaultComputed,
Methods extends MethodOptions = DefaultMethods<Vue>
Methods extends MethodOptions = DefaultMethods<Vue>,
Mixin = {},
Extends = {},
Emits extends EmitsOptions = {}
> = Vue2ComponentOptions<
Vue,
ShallowUnwrapRef<RawBindings> & Data,
Expand All @@ -90,7 +125,16 @@ export type VueProxy<
PropsOptions,
ExtractPropTypes<PropsOptions>
> &
VueConstructorProxy<PropsOptions, RawBindings, Data, Computed, Methods>
VueConstructorProxy<
PropsOptions,
RawBindings,
Data,
Computed,
Methods,
Mixin,
Extends,
Emits
>

// public properties exposed on the proxy, which is used as the render context
// in templates (as `this` in the render option)
Expand Down
11 changes: 6 additions & 5 deletions src/component/defineAsyncComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,16 @@ import {
ComponentOptionsWithProps,
} from './componentOptions'

type ComponentOptions =
type Component = VueProxy<any, any, any, any, any>

type ComponentOrComponentOptions =
// Component
| Component
// ComponentOptions
| ComponentOptionsWithoutProps
| ComponentOptionsWithArrayProps
| ComponentOptionsWithProps

type Component = VueProxy<any, any, any, any, any>

type ComponentOrComponentOptions = ComponentOptions | Component

export type AsyncComponentResolveResult<T = ComponentOrComponentOptions> =
| T
| { default: T } // es modules
Expand Down
94 changes: 79 additions & 15 deletions src/component/defineComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,45 +9,109 @@ import {
import { VueProxy } from './componentProxy'
import { Data } from './common'
import { HasDefined } from '../types/basic'
import { EmitsOptions } from '../runtimeContext'

// overload 1: object format with no props
/**
* overload 1: object format with no props
*/
export function defineComponent<
RawBindings,
D = Data,
C extends ComputedOptions = {},
M extends MethodOptions = {}
M extends MethodOptions = {},
Mixin = {},
Extends = {},
Emits extends EmitsOptions = {}
>(
options: ComponentOptionsWithoutProps<{}, RawBindings, D, C, M>
): VueProxy<{}, RawBindings, D, C, M>

// overload 2: object format with array props declaration
// props inferred as { [key in PropNames]?: any }
// return type is for Vetur and TSX support
options: ComponentOptionsWithoutProps<
{},
RawBindings,
D,
C,
M,
Mixin,
Extends,
Emits
>
): VueProxy<{}, RawBindings, D, C, M, Mixin, Extends, Emits>
/**
* overload 2: object format with array props declaration
* props inferred as `{ [key in PropNames]?: any }`
*
* return type is for Vetur and TSX support
*/
export function defineComponent<
PropNames extends string,
RawBindings = Data,
D = Data,
C extends ComputedOptions = {},
M extends MethodOptions = {},
Mixin = {},
Extends = {},
Emits extends EmitsOptions = {},
PropsOptions extends ComponentPropsOptions = ComponentPropsOptions
>(
options: ComponentOptionsWithArrayProps<PropNames, RawBindings, D, C, M>
): VueProxy<Readonly<{ [key in PropNames]?: any }>, RawBindings, D, C, M>
options: ComponentOptionsWithArrayProps<
PropNames,
RawBindings,
D,
C,
M,
Mixin,
Extends,
Emits
>
): VueProxy<
Readonly<{ [key in PropNames]?: any }>,
RawBindings,
D,
C,
M,
Mixin,
Extends,
Emits
>

// overload 3: object format with object props declaration
// see `ExtractPropTypes` in ./componentProps.ts
/**
* overload 3: object format with object props declaration
*
* see `ExtractPropTypes` in './componentProps.ts'
*/
export function defineComponent<
Props,
RawBindings = Data,
D = Data,
C extends ComputedOptions = {},
M extends MethodOptions = {},
Mixin = {},
Extends = {},
Emits extends EmitsOptions = {},
PropsOptions extends ComponentPropsOptions = ComponentPropsOptions
>(
options: HasDefined<Props> extends true
? ComponentOptionsWithProps<PropsOptions, RawBindings, D, C, M, Props>
: ComponentOptionsWithProps<PropsOptions, RawBindings, D, C, M>
): VueProxy<PropsOptions, RawBindings, D, C, M>
? ComponentOptionsWithProps<
PropsOptions,
RawBindings,
D,
C,
M,
Mixin,
Extends,
Emits,
Props
>
: ComponentOptionsWithProps<
PropsOptions,
RawBindings,
D,
C,
M,
Mixin,
Extends,
Emits
>
): VueProxy<PropsOptions, RawBindings, D, C, M, Mixin, Extends, Emits>

// implementation, close to no-op
export function defineComponent(options: any) {
return options as any
Expand Down
Loading