diff --git a/src/apis/createApp.ts b/src/apis/createApp.ts index ff69cf4f..4e927176 100644 --- a/src/apis/createApp.ts +++ b/src/apis/createApp.ts @@ -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' diff --git a/src/apis/createElement.ts b/src/apis/createElement.ts index 6302405e..79ee9abc 100644 --- a/src/apis/createElement.ts +++ b/src/apis/createElement.ts @@ -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) { diff --git a/src/component/componentOptions.ts b/src/component/componentOptions.ts index cc778625..dd3de46b 100644 --- a/src/component/componentOptions.ts +++ b/src/component/componentOptions.ts @@ -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' @@ -8,13 +8,6 @@ export { ComponentPropsOptions } from './componentProps' export type ComputedGetter = (ctx?: any) => T export type ComputedSetter = (v: T) => void -export type ObjectEmitsOptions = Record< - string, - ((...args: any[]) => any) | null -> - -export type EmitsOptions = ObjectEmitsOptions | string[] - export interface WritableComputedOptions { get: ComputedGetter set: ComputedSetter @@ -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, 'data' | 'computed' | 'method' | 'setup' | 'props' @@ -67,12 +63,17 @@ export type ComponentOptionsWithProps< D = Data, C extends ComputedOptions = {}, M extends MethodOptions = {}, + Mixin = {}, + Extends = {}, + Emits extends EmitsOptions = {}, Props = ExtractPropTypes > = ComponentOptionsBase & { props?: PropsOptions - emits?: (EmitsOptions | string[]) & ThisType + emits?: Emits & ThisType setup?: SetupFunction -} & ThisType> +} & ThisType< + ComponentRenderProxy + > export type ComponentOptionsWithArrayProps< PropNames extends string = string, @@ -80,24 +81,34 @@ export type ComponentOptionsWithArrayProps< D = Data, C extends ComputedOptions = {}, M extends MethodOptions = {}, + Mixin = {}, + Extends = {}, + Emits extends EmitsOptions = {}, Props = Readonly<{ [key in PropNames]?: any }> > = ComponentOptionsBase & { props?: PropNames[] - emits?: (EmitsOptions | string[]) & ThisType + emits?: Emits & ThisType setup?: SetupFunction -} & ThisType> +} & ThisType< + ComponentRenderProxy + > export type ComponentOptionsWithoutProps< Props = {}, RawBindings = Data, D = Data, C extends ComputedOptions = {}, - M extends MethodOptions = {} + M extends MethodOptions = {}, + Mixin = {}, + Extends = {}, + Emits extends EmitsOptions = {} > = ComponentOptionsBase & { props?: undefined - emits?: (EmitsOptions | string[]) & ThisType + emits?: Emits & ThisType setup?: SetupFunction -} & ThisType> +} & ThisType< + ComponentRenderProxy + > export type WithLegacyAPI = T & Omit, keyof T> diff --git a/src/component/componentProxy.ts b/src/component/componentProxy.ts index fbf81d4c..39202732 100644 --- a/src/component/componentProxy.ts +++ b/src/component/componentProxy.ts @@ -21,9 +21,29 @@ import { ComponentInternalInstance, EmitFn, EmitsOptions, + ObjectEmitsOptions, Slots, } from '../runtimeContext' +type EmitsToProps = T extends string[] + ? { + [K in string & `on${Capitalize}`]?: (...args: any[]) => any + } + : T extends ObjectEmitsOptions + ? { + [K in string & + `on${Capitalize}`]?: K extends `on${infer C}` + ? T[Uncapitalize] extends null + ? (...args: any[]) => any + : ( + ...args: T[Uncapitalize] extends (...args: infer P) => any + ? P + : never + ) => any + : never + } + : {} + export type ComponentInstance = InstanceType // public properties exposed on the proxy, which is used as the render context @@ -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 @@ -45,12 +68,13 @@ export type ComponentRenderProxy< : P & PublicProps > $attrs: Data + $emit: EmitFn } & Readonly

& ShallowUnwrapRef & D & M & ExtractComputedReturns & - Omit + Omit // for Vetur and TSX support type VueConstructorProxy< @@ -58,15 +82,23 @@ type VueConstructorProxy< RawBindings, Data, Computed extends ComputedOptions, - Methods extends MethodOptions -> = VueConstructor & { + Methods extends MethodOptions, + Mixin = {}, + Extends = {}, + Emits extends EmitsOptions = {}, + Props = ExtractPropTypes & + ({} extends Emits ? {} : EmitsToProps) +> = Omit & { new (...args: any[]): ComponentRenderProxy< - ExtractPropTypes, + Props, ShallowUnwrapRef, Data, Computed, Methods, - ExtractPropTypes, + Mixin, + Extends, + Emits, + Props, ExtractDefaultPropTypes, true > @@ -81,7 +113,10 @@ export type VueProxy< RawBindings, Data = DefaultData, Computed extends ComputedOptions = DefaultComputed, - Methods extends MethodOptions = DefaultMethods + Methods extends MethodOptions = DefaultMethods, + Mixin = {}, + Extends = {}, + Emits extends EmitsOptions = {} > = Vue2ComponentOptions< Vue, ShallowUnwrapRef & Data, @@ -90,7 +125,16 @@ export type VueProxy< PropsOptions, ExtractPropTypes > & - VueConstructorProxy + 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) diff --git a/src/component/defineAsyncComponent.ts b/src/component/defineAsyncComponent.ts index 2dd24db2..a5c40894 100644 --- a/src/component/defineAsyncComponent.ts +++ b/src/component/defineAsyncComponent.ts @@ -8,15 +8,16 @@ import { ComponentOptionsWithProps, } from './componentOptions' -type ComponentOptions = +type Component = VueProxy + +type ComponentOrComponentOptions = + // Component + | Component + // ComponentOptions | ComponentOptionsWithoutProps | ComponentOptionsWithArrayProps | ComponentOptionsWithProps -type Component = VueProxy - -type ComponentOrComponentOptions = ComponentOptions | Component - export type AsyncComponentResolveResult = | T | { default: T } // es modules diff --git a/src/component/defineComponent.ts b/src/component/defineComponent.ts index 36359147..a6a035f7 100644 --- a/src/component/defineComponent.ts +++ b/src/component/defineComponent.ts @@ -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 -): VueProxy, 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 extends true - ? ComponentOptionsWithProps - : ComponentOptionsWithProps -): VueProxy + ? ComponentOptionsWithProps< + PropsOptions, + RawBindings, + D, + C, + M, + Mixin, + Extends, + Emits, + Props + > + : ComponentOptionsWithProps< + PropsOptions, + RawBindings, + D, + C, + M, + Mixin, + Extends, + Emits + > +): VueProxy + // implementation, close to no-op export function defineComponent(options: any) { return options as any diff --git a/test-dts/defineComponent.test-d.ts b/test-dts/defineComponent.test-d.ts index dd5f7ac9..d4641a9a 100644 --- a/test-dts/defineComponent.test-d.ts +++ b/test-dts/defineComponent.test-d.ts @@ -7,6 +7,7 @@ import { defineComponent, PropType, h, + createApp, } from './index' describe('with object props', () => { @@ -588,24 +589,22 @@ describe('extends with mixins', () => { }) describe('compatibility w/ createApp', () => { - /* const comp = defineComponent({}) createApp(comp).mount('#hello') const comp2 = defineComponent({ - props: { foo: String } + props: { foo: String }, }) createApp(comp2).mount('#hello') const comp3 = defineComponent({ setup() { return { - a: 1 + a: 1, } - } + }, }) createApp(comp3).mount('#hello') - */ }) describe('defineComponent', () => { @@ -618,8 +617,6 @@ describe('defineComponent', () => { }) describe('emits', () => { - /* - // Note: for TSX inference, ideally we want to map emits to onXXX props, // but that requires type-level string constant concatenation as suggested in // https://github.com/Microsoft/TypeScript/issues/12754 @@ -633,9 +630,10 @@ describe('emits', () => { defineComponent({ emits: { click: (n: number) => typeof n === 'number', - input: (b: string) => null + input: (b: string) => null, }, setup(props, { emit }) { + /* emit('click', 1) emit('input', 'foo') // @ts-expect-error @@ -648,6 +646,7 @@ describe('emits', () => { expectError(emit('input')) // @ts-expect-error expectError(emit('input', 1)) + */ }, created() { this.$emit('click', 1) @@ -662,9 +661,10 @@ describe('emits', () => { expectError(this.$emit('input')) // @ts-expect-error expectError(this.$emit('input', 1)) - } + }, }) + /* // with array emits defineComponent({ emits: ['foo', 'bar'],