Skip to content

Commit

Permalink
feat: add component $emit typing support (#846)
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaoxiangmoe authored Nov 7, 2021
1 parent 0fbc572 commit b980175
Show file tree
Hide file tree
Showing 14 changed files with 1,327 additions and 774 deletions.
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
File renamed without changes.
Loading

0 comments on commit b980175

Please sign in to comment.