diff --git a/package.json b/package.json index ea8d44bb95f..bf91637abc0 100644 --- a/package.json +++ b/package.json @@ -79,7 +79,7 @@ "semver": "^7.3.2", "serve": "^12.0.0", "ts-jest": "^26.2.0", - "typescript": "^4.2.2", + "typescript": "^4.4.2", "yorkie": "^2.0.0" } } diff --git a/packages/compiler-core/__tests__/transforms/vSlot.spec.ts b/packages/compiler-core/__tests__/transforms/vSlot.spec.ts index 4a7df1e8dfe..687c4d8b358 100644 --- a/packages/compiler-core/__tests__/transforms/vSlot.spec.ts +++ b/packages/compiler-core/__tests__/transforms/vSlot.spec.ts @@ -780,9 +780,7 @@ describe('compiler: transform component slots', () => { }) test('', () => { - const { slots } = parseWithSlots( - `` - ) + const { slots } = parseWithSlots(``) expect(slots).toMatchObject(toMatch) }) }) diff --git a/packages/compiler-sfc/src/cssVars.ts b/packages/compiler-sfc/src/cssVars.ts index f51269f1269..3032e2fbf5d 100644 --- a/packages/compiler-sfc/src/cssVars.ts +++ b/packages/compiler-sfc/src/cssVars.ts @@ -12,7 +12,8 @@ import { PluginCreator } from 'postcss' import hash from 'hash-sum' export const CSS_VARS_HELPER = `useCssVars` -export const cssVarRE = /\bv-bind\(\s*(?:'([^']+)'|"([^"]+)"|([^'"][^)]*))\s*\)/g +export const cssVarRE = + /\bv-bind\(\s*(?:'([^']+)'|"([^"]+)"|([^'"][^)]*))\s*\)/g export function genCssVarsFromList( vars: string[], diff --git a/packages/runtime-core/src/apiDefineComponent.ts b/packages/runtime-core/src/apiDefineComponent.ts index f6793da5f30..854ea69832e 100644 --- a/packages/runtime-core/src/apiDefineComponent.ts +++ b/packages/runtime-core/src/apiDefineComponent.ts @@ -1,77 +1,70 @@ import { ComputedOptions, MethodOptions, - ComponentOptionsWithoutProps, - ComponentOptionsWithArrayProps, - ComponentOptionsWithObjectProps, ComponentOptionsMixin, RenderFunction, - ComponentOptionsBase + BettterComponentOptionsWithObjectProps, + BetterComponentOptions, + BettterComponentOptionsWithArrayProps, + BettterComponentOptionsWithoutProps } from './componentOptions' import { SetupContext, AllowedComponentProps, - ComponentCustomProps + ComponentCustomProps, + Component, + BetterComponent } from './component' import { ExtractPropTypes, - ComponentPropsOptions, - ExtractDefaultPropTypes + ExtractDefaultPropTypes, + ComponentObjectPropsOptions } from './componentProps' -import { EmitsOptions, EmitsToProps } from './componentEmits' +import { EmitsOptions } from './componentEmits' import { isFunction } from '@vue/shared' import { VNodeProps } from './vnode' -import { - CreateComponentPublicInstance, - ComponentPublicInstanceConstructor -} from './componentPublicInstance' +import { RenderComponent } from './componentPublicInstance' +import { Slots } from './componentSlots' +import { Directive } from './directives' export type PublicProps = VNodeProps & AllowedComponentProps & ComponentCustomProps +// Type Helper for defineComponent return export type DefineComponent< - PropsOrPropOptions = {}, + Props extends Record, + Emits extends EmitsOptions = {}, + S = {}, + LC extends Record = {}, + LD extends Record = {}, RawBindings = {}, D = {}, - C extends ComputedOptions = ComputedOptions, - M extends MethodOptions = MethodOptions, + C extends ComputedOptions = {}, + M extends MethodOptions = {}, + Exposed extends string = string, Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, Extends extends ComponentOptionsMixin = ComponentOptionsMixin, - E extends EmitsOptions = Record, - EE extends string = string, - PP = PublicProps, - Props = Readonly> & EmitsToProps, - Defaults = ExtractDefaultPropTypes -> = ComponentPublicInstanceConstructor< - CreateComponentPublicInstance< - Props, - RawBindings, - D, - C, - M, - Mixin, - Extends, - E, - PP & Props, - Defaults, - true - > & - Props + Defaults = {}, + Options = any +> = BetterComponent< + Props, + Emits, + S, + // TODO ADD Similar binding as the BetterCreateComponentPublicInstance + {}, + LC, + LD, + D, + RawBindings, + C, + M, + Exposed, + Mixin, + Extends, + Options > & - ComponentOptionsBase< - Props, - RawBindings, - D, - C, - M, - Mixin, - Extends, - E, - EE, - Defaults - > & - PP + RenderComponent // defineComponent is a utility that is primarily used for type inference // when declaring components. Type inference is provided in the component @@ -80,104 +73,249 @@ export type DefineComponent< // overload 1: direct setup function // (uses user defined props interface) -export function defineComponent( +export function defineComponent< + Props extends Record = {}, + RawBindings = {}, + Emits extends EmitsOptions = {}, + S = {}, + Exposed extends string = string +>( setup: ( props: Readonly, - ctx: SetupContext + ctx: SetupContext> ) => RawBindings | RenderFunction -): DefineComponent +): DefineComponent< + Props, + Emits, + S, + {}, + {}, + RawBindings, + {}, + {}, + {}, + Exposed, + {}, + {}, + {}, + BetterComponentOptions< + Props, + Emits, + S, + {}, + {}, + RawBindings, + {}, + {}, + {}, + string, + Exposed + > +> // overload 2: object format with no props // (uses user defined props interface) // return type is for Vetur and TSX support export function defineComponent< - Props = {}, + Emits extends EmitsOptions = {}, + S = {}, + LC extends Record = {}, + LD extends Record = {}, RawBindings = {}, D = {}, C extends ComputedOptions = {}, M extends MethodOptions = {}, + EE extends string = string, + Exposed extends string = string, Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, Extends extends ComponentOptionsMixin = ComponentOptionsMixin, - E extends EmitsOptions = EmitsOptions, - EE extends string = string + Defaults = {} >( - options: ComponentOptionsWithoutProps< - Props, + options: BettterComponentOptionsWithoutProps< + Emits, + S, + LC, + LD, RawBindings, D, C, M, + EE, + Exposed, Mixin, Extends, - E, - EE + Defaults > -): DefineComponent - +): DefineComponent< + {}, + Emits, + S, + LC, + LD, + RawBindings, + D, + C, + M, + Exposed, + Mixin, + Extends, + Defaults, + BettterComponentOptionsWithoutProps< + Emits, + S, + LC, + LD, + RawBindings, + D, + C, + M, + EE, + Exposed, + Mixin, + Extends, + Defaults + > +> // overload 3: 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, - D, + Emits extends EmitsOptions = {}, + S = {}, + LC extends Record = {}, + LD extends Record = {}, + RawBindings = {}, + D = {}, C extends ComputedOptions = {}, M extends MethodOptions = {}, + EE extends string = string, + Exposed extends string = string, Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, Extends extends ComponentOptionsMixin = ComponentOptionsMixin, - E extends EmitsOptions = Record, - EE extends string = string + Defaults = {}, + Props extends Record = Readonly<{ [key in PropNames]?: any }> >( - options: ComponentOptionsWithArrayProps< - PropNames, + options: BettterComponentOptionsWithArrayProps< + Props, + Emits, + S, + LC, + LD, RawBindings, D, C, M, + EE, + Exposed, Mixin, Extends, - E, - EE + Defaults, + PropNames > ): DefineComponent< - Readonly<{ [key in PropNames]?: any }>, + Props, + Emits, + S, + LC, + LD, RawBindings, D, C, M, + Exposed, Mixin, Extends, - E, - EE + Defaults, + BettterComponentOptionsWithArrayProps< + Props, + Emits, + S, + LC, + LD, + RawBindings, + D, + C, + M, + EE, + Exposed, + Mixin, + Extends, + Defaults, + PropNames + > > // overload 4: object format with object props declaration // see `ExtractPropTypes` in ./componentProps.ts export function defineComponent< - // the Readonly constraint allows TS to treat the type of { required: true } - // as constant instead of boolean. - PropsOptions extends Readonly, - RawBindings, - D, + PropsOptions extends ComponentObjectPropsOptions = ComponentObjectPropsOptions, + Emits extends EmitsOptions = {}, + S = {}, + LC extends Record = {}, + LD extends Record = {}, + RawBindings = {}, + D = {}, C extends ComputedOptions = {}, M extends MethodOptions = {}, + EE extends string = string, + Exposed extends string = string, Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, Extends extends ComponentOptionsMixin = ComponentOptionsMixin, - E extends EmitsOptions = Record, - EE extends string = string + Defaults = ExtractDefaultPropTypes, + Props extends Record = Readonly< + ExtractPropTypes + > >( - options: ComponentOptionsWithObjectProps< - PropsOptions, + options: BettterComponentOptionsWithObjectProps< + Props, + Emits, + S, + LC, + LD, + RawBindings, + D, + C, + M, + EE, + Exposed, + Mixin, + Extends, + Defaults, + PropsOptions + > +): DefineComponent< + Props, + Emits, + S, + LC, + LD, + RawBindings, + D, + C, + M, + Exposed, + Mixin, + Extends, + Defaults, + BettterComponentOptionsWithObjectProps< + Props, + Emits, + S, + LC, + LD, RawBindings, D, C, M, + EE, + Exposed, Mixin, Extends, - E, - EE + Defaults, + PropsOptions > -): DefineComponent +> // implementation, close to no-op export function defineComponent(options: unknown) { diff --git a/packages/runtime-core/src/compat/componentFunctional.ts b/packages/runtime-core/src/compat/componentFunctional.ts index 80af32a1dae..8e561250d01 100644 --- a/packages/runtime-core/src/compat/componentFunctional.ts +++ b/packages/runtime-core/src/compat/componentFunctional.ts @@ -13,7 +13,9 @@ const normalizedFunctionalComponentMap = new Map< FunctionalComponent >() -export const legacySlotProxyHandlers: ProxyHandler = { +export const legacySlotProxyHandlers: ProxyHandler< + InternalSlots> +> = { get(target, key: string) { const slot = target[key] return slot && slot() diff --git a/packages/runtime-core/src/component.ts b/packages/runtime-core/src/component.ts index 16b2333051d..952cf6221d6 100644 --- a/packages/runtime-core/src/component.ts +++ b/packages/runtime-core/src/component.ts @@ -23,7 +23,8 @@ import { ComponentPropsOptions, NormalizedPropsOptions, initProps, - normalizePropsOptions + normalizePropsOptions, + Prop } from './componentProps' import { Slots, initSlots, InternalSlots } from './componentSlots' import { warn } from './warning' @@ -32,7 +33,10 @@ import { AppContext, createAppContext, AppConfig } from './apiCreateApp' import { Directive, validateDirectiveName } from './directives' import { applyOptions, + BetterComponentOptions, + BetterComponentOptionsMixin, ComponentOptions, + ComponentOptionsMixin, ComputedOptions, MethodOptions } from './componentOptions' @@ -70,6 +74,40 @@ export type Data = Record */ export interface ComponentCustomProps {} +/** + * For globally defined Directives + * Here is an example of adding a directive `VTooltip` as global directive: + * + * @example + * ```ts + * import VTooltip from 'v-tooltip' + * + * declare module '@vue/runtime-core' { + * interface GlobalDirectives { + * VTooltip + * } + * } + * ``` + */ +export interface GlobalDirectives extends Record {} + +/** + * For globally defined Components + * Here is an example of adding a component `RouterView` as global component: + * + * @example + * ```ts + * import { RouterView } from 'vue-router' + * + * declare module '@vue/runtime-core' { + * interface GlobalComponents { + * RouterView + * } + * } + * ``` + */ +export interface GlobalComponents extends Record {} + /** * Default allowed non-declared props on component in TSX */ @@ -103,10 +141,13 @@ export interface ComponentInternalOptions { __file?: string } -export interface FunctionalComponent

- extends ComponentInternalOptions { +export interface FunctionalComponent< + P = {}, + E extends EmitsOptions = {}, + S extends Slots = Slots +> extends ComponentInternalOptions { // use of any here is intentional so it can be a valid JSX Element constructor - (props: P, ctx: Omit, 'expose'>): any + (props: P, ctx: Omit, 'expose'>): any props?: ComponentPropsOptions

emits?: E | (keyof E)[] inheritAttrs?: boolean @@ -129,10 +170,13 @@ export type ConcreteComponent< RawBindings = any, D = any, C extends ComputedOptions = ComputedOptions, - M extends MethodOptions = MethodOptions + M extends MethodOptions = MethodOptions, + Mixin extends ComponentOptionsMixin = any, + Extends extends ComponentOptionsMixin = any, + E extends EmitsOptions = any > = - | ComponentOptions - | FunctionalComponent + | ComponentOptions + | FunctionalComponent /** * A type used in public APIs where a component type is expected. @@ -148,6 +192,43 @@ export type Component< | ConcreteComponent | ComponentPublicInstanceConstructor +// export type BetterConcreteComponent< +// Props extends Record> = {}, +// Emits extends EmitsOptions = {}, +// Slots = {}, +// // Allows to expose public API properties, this bypasses Data/Computed/Methods, +// // easier to declare manually +// Bindings extends Record = {}, +// Options extends ComponentOptions = {} + +// > =; + +export type BetterComponent< + Props extends Record = {}, + Emits extends EmitsOptions = {}, + Slots = {}, + // Allows to expose public API properties, this bypasses Data/Computed/Methods, + // easier to declare manually + Bindings extends Record = {}, + LocalComponents extends Record = {}, + LocalDirectives extends Record = {}, + D = any, + RawBindings = any, + C extends ComputedOptions = ComputedOptions, + M extends MethodOptions = MethodOptions, + Exposed extends string = string, + Mixin extends BetterComponentOptionsMixin = any, + Extends extends BetterComponentOptionsMixin = any, + Options extends BetterComponentOptions = any +> = + | ConcreteComponent + | ComponentPublicInstanceConstructor + | { supa: Options } + +const ddd: BetterComponent = { + setup() {} +} + export { ComponentOptions } type LifecycleHook = TFn[] | null @@ -169,10 +250,14 @@ export const enum LifecycleHooks { SERVER_PREFETCH = 'sp' } -export interface SetupContext { +export interface SetupContext< + E = EmitsOptions, + P = {}, + S extends Slots = Slots +> { attrs: Data - slots: Slots - emit: EmitFn + slots: S + emit: EmitFn expose: (exposed?: Record) => void } diff --git a/packages/runtime-core/src/componentEmits.ts b/packages/runtime-core/src/componentEmits.ts index 390c6350b8c..59553af2a76 100644 --- a/packages/runtime-core/src/componentEmits.ts +++ b/packages/runtime-core/src/componentEmits.ts @@ -53,10 +53,34 @@ export type EmitsToProps = T extends string[] } : {} +// declare function processEmits(a: T): EmitsToProps + +// const x = processEmits({ click: (a: 1) => true }) + +// declare const a: EmitsToProps<'click'[] & 'bar'[]> +// a.onClick + +// type Unpacked = T extends (infer U)[] +// ? U +// : T extends (...args: any[]) => infer U +// ? U +// : T extends Promise +// ? U +// : T +// type SSS = T extends string[] ? T : false + +// declare const t: SSS<'click'[] & 'bar'[]> + +export type EmitVModelUpdate< + T, + E extends keyof T & string = keyof T & string +> = (event: `update:${E}`, value: T[E]) => void + export type EmitFn< Options = ObjectEmitsOptions, + P = {}, Event extends keyof Options = keyof Options -> = Options extends Array +> = (Options extends Array ? (event: V, ...args: any[]) => void : {} extends Options // if the emit is empty object (usually the default value for emit) should be converted to function ? (event: string, ...args: any[]) => void @@ -66,7 +90,8 @@ export type EmitFn< ? (event: key, ...args: Args) => void : (event: key, ...args: any[]) => void }[Event] - > + >) & + EmitVModelUpdate

export function emit( instance: ComponentInternalInstance, diff --git a/packages/runtime-core/src/componentOptions.ts b/packages/runtime-core/src/componentOptions.ts index db12bfbcf51..e9898e83a2c 100644 --- a/packages/runtime-core/src/componentOptions.ts +++ b/packages/runtime-core/src/componentOptions.ts @@ -55,7 +55,8 @@ import { EmitsOptions, EmitsToProps } from './componentEmits' import { Directive } from './directives' import { CreateComponentPublicInstance, - ComponentPublicInstance + ComponentPublicInstance, + BetterCreateComponentPublicInstance } from './componentPublicInstance' import { warn } from './warning' import { VNodeChild } from './vnode' @@ -69,6 +70,8 @@ import { softAssertCompatEnabled } from './compat/compatConfig' import { OptionMergeFunction } from './apiCreateApp' +import { Slots } from './componentSlots' +import { PublicProps } from './apiDefineComponent' /** * Interface for declaring custom options. @@ -98,13 +101,117 @@ type ExtractOptionProp = T extends ComponentOptionsBase< any, // M any, // Mixin any, // Extends - any // EmitsOptions + infer E // EmitsOptions > ? unknown extends P ? {} - : P + : P & EmitsToProps : {} +export interface BetterComponentOptionsBase< + Props extends Record, + Emits extends EmitsOptions, + S, + LC extends Record, + LD extends Record, + RawBindings, + D, + C extends ComputedOptions, + M extends MethodOptions, + EE extends string = string, + Exposed extends string = string, + Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, + Extends extends ComponentOptionsMixin = ComponentOptionsMixin, + Defaults = {} +> extends LegacyOptions, + ComponentInternalOptions, + ComponentCustomOptions { + setup?: ( + this: void, + props: Readonly< + LooseRequired< + Props & + UnionToIntersection> & + UnionToIntersection> & + EmitsToProps + > + >, + ctx: SetupContext> + ) => Promise | RawBindings | RenderFunction | void + + name?: string + template?: string | object // can be a direct DOM node + // Note: we are intentionally using the signature-less `Function` type here + // since any type with signature will cause the whole inference to fail when + // the return expression contains reference to `this`. + // Luckily `render()` doesn't need any arguments nor does it care about return + // type. + render?: Function + components?: LC + directives?: LD + inheritAttrs?: boolean + emits?: (Emits & ThisType) | EE[] + expose?: Exposed[] + serverPrefetch?(): Promise + + // Runtime compiler only ----------------------------------------------------- + compilerOptions?: RuntimeCompilerOptions + + slots?: S & ThisType + + // Internal ------------------------------------------------------------------ + + /** + * SSR only. This is produced by compiler-ssr and attached in compiler-sfc + * not user facing, so the typing is lax and for test only. + * @internal + */ + ssrRender?: ( + ctx: any, + push: (item: any) => void, + parentInstance: ComponentInternalInstance, + attrs: Data | undefined, + // for compiler-optimized bindings + $props: ComponentInternalInstance['props'], + $setup: ComponentInternalInstance['setupState'], + $data: ComponentInternalInstance['data'], + $options: ComponentInternalInstance['ctx'] + ) => void + + /** + * Only generated by compiler-sfc to mark a ssr render function inlined and + * returned from setup() + * @internal + */ + __ssrInlineRender?: boolean + + /** + * marker for AsyncComponentWrapper + * @internal + */ + __asyncLoader?: () => Promise + /** + * the inner component resolved by the AsyncComponentWrapper + * @internal + */ + __asyncResolved?: ConcreteComponent + + // Type differentiators ------------------------------------------------------ + + // Note these are internal but need to be exposed in d.ts for type inference + // to work! + + // type-only differentiator to separate OptionWithoutProps from a constructor + // type returned by defineComponent() or FunctionalComponent + call?: (this: unknown, ...args: unknown[]) => never + // type-only differentiators for built-in Vnode types + __isFragment?: never + __isTeleport?: never + __isSuspense?: never + + __defaults?: Defaults +} + export interface ComponentOptionsBase< Props, RawBindings, @@ -113,10 +220,14 @@ export interface ComponentOptionsBase< M extends MethodOptions, Mixin extends ComponentOptionsMixin, Extends extends ComponentOptionsMixin, - E extends EmitsOptions, + E extends EmitsOptions = {}, EE extends string = string, + S = any, + LC extends Record = {}, + Directives extends Record = {}, + Exposed extends string = string, Defaults = {} -> extends LegacyOptions, +> extends LegacyOptions, ComponentInternalOptions, ComponentCustomOptions { setup?: ( @@ -125,10 +236,11 @@ export interface ComponentOptionsBase< LooseRequired< Props & UnionToIntersection> & - UnionToIntersection> + UnionToIntersection> & + EmitsToProps > >, - ctx: SetupContext + ctx: SetupContext> ) => Promise | RawBindings | RenderFunction | void name?: string template?: string | object // can be a direct DOM node @@ -138,17 +250,18 @@ export interface ComponentOptionsBase< // Luckily `render()` doesn't need any arguments nor does it care about return // type. render?: Function - components?: Record - directives?: Record + components?: LC + directives?: Directives inheritAttrs?: boolean - emits?: (E | EE[]) & ThisType - // TODO infer public instance type based on exposed keys - expose?: string[] + emits?: (E & ThisType) | EE[] + expose?: Exposed[] serverPrefetch?(): Promise // Runtime compiler only ----------------------------------------------------- compilerOptions?: RuntimeCompilerOptions + slots?: S & ThisType + // Internal ------------------------------------------------------------------ /** @@ -222,6 +335,10 @@ export type ComponentOptionsWithoutProps< Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = EmitsOptions, EE extends string = string, + S = any, + LC extends Record = {}, + Directives extends Record = {}, + Exposed extends string = string, PE = Props & EmitsToProps > = ComponentOptionsBase< PE, @@ -233,11 +350,31 @@ export type ComponentOptionsWithoutProps< Extends, E, EE, + S, + LC, + Directives, + Exposed, {} > & { props?: undefined } & ThisType< - CreateComponentPublicInstance + CreateComponentPublicInstance< + Props, + RawBindings, + D, + C, + M, + Mixin, + Extends, + E, + S, + Props, + {}, + false, + LC, + Directives, + Exposed + > > export type ComponentOptionsWithArrayProps< @@ -250,7 +387,11 @@ export type ComponentOptionsWithArrayProps< Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = EmitsOptions, EE extends string = string, - Props = Readonly<{ [key in PropNames]?: any }> & EmitsToProps + S = any, + LC extends Record = {}, + Directives extends Record = {}, + Exposed extends string = string, + Props = Readonly<{ [key in PropNames]?: any }> > = ComponentOptionsBase< Props, RawBindings, @@ -261,6 +402,10 @@ export type ComponentOptionsWithArrayProps< Extends, E, EE, + S, + LC, + Directives, + Exposed, {} > & { props: PropNames[] @@ -273,7 +418,14 @@ export type ComponentOptionsWithArrayProps< M, Mixin, Extends, - E + E, + S, + Props, + {}, + false, + LC, + Directives, + Exposed > > @@ -285,9 +437,13 @@ export type ComponentOptionsWithObjectProps< M extends MethodOptions = {}, Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, Extends extends ComponentOptionsMixin = ComponentOptionsMixin, - E extends EmitsOptions = EmitsOptions, + E extends EmitsOptions = {}, EE extends string = string, - Props = Readonly> & EmitsToProps, + S = any, + LC extends Record = {}, + Directives extends Record = {}, + Exposed extends string = string, + Props = Readonly>, Defaults = ExtractDefaultPropTypes > = ComponentOptionsBase< Props, @@ -299,6 +455,10 @@ export type ComponentOptionsWithObjectProps< Extends, E, EE, + S, + LC, + Directives, + Exposed, Defaults > & { props: PropsOptions & ThisType @@ -312,9 +472,13 @@ export type ComponentOptionsWithObjectProps< Mixin, Extends, E, + S, Props, - Defaults, - false + {}, + false, + LC, + Directives, + Exposed > > @@ -326,11 +490,29 @@ export type ComponentOptions< M extends MethodOptions = any, Mixin extends ComponentOptionsMixin = any, Extends extends ComponentOptionsMixin = any, - E extends EmitsOptions = any -> = ComponentOptionsBase & + E extends EmitsOptions = {}, + S = any, + LC extends Record = {}, + Directives extends Record = {}, + Exposed extends string = string +> = ComponentOptionsBase< + Props, + RawBindings, + D, + C, + M, + Mixin, + Extends, + E, + string, + S, + LC, + Directives, + Exposed +> & ThisType< CreateComponentPublicInstance< - {}, + Props, RawBindings, D, C, @@ -338,12 +520,254 @@ export type ComponentOptions< Mixin, Extends, E, - Readonly + S, + Props, + {}, + false, + LC, + Directives, + Exposed + > + > + +export type BettterComponentOptionsWithoutProps< + Emits extends EmitsOptions = {}, + S = {}, + LC extends Record = {}, + LD extends Record = {}, + RawBindings = {}, + D = {}, + C extends ComputedOptions = {}, + M extends MethodOptions = {}, + EE extends string = string, + Exposed extends string = string, + Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, + Extends extends ComponentOptionsMixin = ComponentOptionsMixin, + Defaults = {} +> = BetterComponentOptions< + {}, + Emits, + S, + LC, + LD, + RawBindings, + D, + C, + M, + EE, + Exposed, + Mixin, + Extends, + Defaults +> & { + props?: null +} + +export type BettterComponentOptionsWithArrayProps< + Props extends Record = {}, + Emits extends EmitsOptions = {}, + S = {}, + LC extends Record = {}, + LD extends Record = {}, + RawBindings = {}, + D = {}, + C extends ComputedOptions = {}, + M extends MethodOptions = {}, + EE extends string = string, + Exposed extends string = string, + Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, + Extends extends ComponentOptionsMixin = ComponentOptionsMixin, + Defaults = {}, + PropNames extends string = string +> = BetterComponentOptions< + Props, + Emits, + S, + LC, + LD, + RawBindings, + D, + C, + M, + EE, + Exposed, + Mixin, + Extends, + Defaults +> & { + props: PropNames[] +} + +export type BettterComponentOptionsWithObjectProps< + Props extends Record = {}, + Emits extends EmitsOptions = {}, + S = {}, + LC extends Record = {}, + LD extends Record = {}, + RawBindings = {}, + D = {}, + C extends ComputedOptions = {}, + M extends MethodOptions = {}, + EE extends string = string, + Exposed extends string = string, + Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, + Extends extends ComponentOptionsMixin = ComponentOptionsMixin, + Defaults = {}, + PropsOptions extends ComponentObjectPropsOptions = ComponentObjectPropsOptions +> = BetterComponentOptions< + Props, + Emits, + S, + LC, + LD, + RawBindings, + D, + C, + M, + EE, + Exposed, + Mixin, + Extends, + Defaults +> & { + props: PropsOptions & ThisType +} + +// export type BetterComponentOptionsTyped< +// Props extends Record = {}, +// Emits extends EmitsOptions = {}, +// S = {}, +// LC extends Record = {}, +// LD extends Record = {}, +// RawBindings = {}, +// D = {}, +// C extends ComputedOptions = {}, +// M extends MethodOptions = {}, +// EE extends string = string, +// Exposed extends string = string, +// Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, +// Extends extends ComponentOptionsMixin = ComponentOptionsMixin, +// Defaults = {} +// > = BetterComponentOptionsBase< +// Props, +// Emits, +// S, +// LC, +// LD, +// RawBindings, +// D, +// C, +// M, +// EE, +// Exposed, +// Mixin, +// Extends, +// Defaults +// > & +// ThisType< +// BetterCreateComponentPublicInstance< +// Props, +// Emits, +// S, +// LC, +// LD, +// RawBindings, +// D, +// C, +// M, +// Exposed, +// Mixin, +// Extends, +// PublicProps, +// Defaults +// > +// > + +export type BetterComponentOptions< + Props extends Record = {}, + Emits extends EmitsOptions = {}, + S = {}, + LC extends Record = {}, + LD extends Record = {}, + RawBindings = {}, + D = {}, + C extends ComputedOptions = {}, + M extends MethodOptions = {}, + EE extends string = string, + Exposed extends string = string, + Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, + Extends extends ComponentOptionsMixin = ComponentOptionsMixin, + Defaults = {} +> = BetterComponentOptionsBase< + Props, + Emits, + S, + LC, + LD, + RawBindings, + D, + C, + M, + EE, + Exposed, + Mixin, + Extends, + Defaults +> & + ThisType< + BetterCreateComponentPublicInstance< + Props, + Emits, + S, + LC, + LD, + RawBindings, + D, + C, + M, + Exposed, + Mixin, + Extends, + PublicProps, + Defaults > > export type ComponentOptionsMixin = ComponentOptionsBase< any, + any, + any, + any, + any, + any, + any, + any, + any, + any, + any, + any, + any, + any +> + +export type BetterComponentOptionsAny = BetterComponentOptions< + any, + any, + any, + any, + any, + any, + any, + any, + any, + any, + any, + any, + any, + any +> + +export type BetterComponentOptionsMixin = BetterComponentOptionsBase< any, any, any, @@ -394,6 +818,7 @@ interface LegacyOptions< D, C extends ComputedOptions, M extends MethodOptions, + E extends EmitsOptions, Mixin extends ComponentOptionsMixin, Extends extends ComponentOptionsMixin > { @@ -414,7 +839,8 @@ interface LegacyOptions< {}, MethodOptions, Mixin, - Extends + Extends, + E >, vm: CreateComponentPublicInstance< Props, @@ -423,7 +849,8 @@ interface LegacyOptions< {}, MethodOptions, Mixin, - Extends + Extends, + E > ) => D computed?: C @@ -500,7 +927,7 @@ export type MergedComponentOptionsOverride = { errorCaptured?: MergedHook } -export type OptionTypesKeys = 'P' | 'B' | 'D' | 'C' | 'M' | 'Defaults' +export type OptionTypesKeys = 'P' | 'B' | 'D' | 'C' | 'M' | 'E' | 'Defaults' export type OptionTypesType< P = {}, @@ -508,9 +935,44 @@ export type OptionTypesType< D = {}, C extends ComputedOptions = {}, M extends MethodOptions = {}, + E extends EmitsOptions = {}, + Defaults = {} +> = { + P: P + B: B + D: D + C: C + M: M + E: E + Defaults: Defaults +} + +export type BetterOptionTypesKeys = + | 'P' + | 'E' + | 'LC' + | 'LD' + | 'B' + | 'D' + | 'C' + | 'M' + | 'Defaults' + +export type BetterOptionTypesType< + P extends Record = {}, + E extends EmitsOptions = {}, + LC extends Record = {}, + LD extends Record = {}, + B = {}, + D = {}, + C extends ComputedOptions = {}, + M extends MethodOptions = {}, Defaults = {} > = { P: P + E: E + LC: LC + LD: LD B: B D: D C: C diff --git a/packages/runtime-core/src/componentProps.ts b/packages/runtime-core/src/componentProps.ts index 7d7f3a38728..319ff59935d 100644 --- a/packages/runtime-core/src/componentProps.ts +++ b/packages/runtime-core/src/componentProps.ts @@ -97,7 +97,7 @@ type DefaultKeys = { : never }[keyof T] -type InferPropType = [T] extends [null] +export type InferPropType = [T] extends [null] ? any // null & true would fail to infer : [T] extends [{ type: null | true }] ? any // As TS issue https://github.com/Microsoft/TypeScript/issues/14829 // somehow `ObjectConstructor` when inferred from { (): T } becomes `any` // `BooleanConstructor` when inferred from PropConstructor(with PropMethod) becomes `Boolean` @@ -113,12 +113,20 @@ type InferPropType = [T] extends [null] : V : T -export type ExtractPropTypes = O extends object +export type ExtractPropTypesOld = O extends object ? { [K in keyof O]?: unknown } & // This is needed to keep the relation between the option prop and the props, allowing to use ctrl+click to navigate to the prop options. see: #3656 { [K in RequiredKeys]: InferPropType } & { [K in OptionalKeys]?: InferPropType } : { [K in string]: any } +export type ExtractPropTypes> = O extends object + ? { + [K in keyof O]: K extends RK + ? InferPropType + : InferPropType | undefined + } + : {} // or { [K in string]: any } //TODO CR check + const enum BooleanFlags { shouldCast, shouldCastTrue diff --git a/packages/runtime-core/src/componentPublicInstance.ts b/packages/runtime-core/src/componentPublicInstance.ts index aad6207ec46..486a3ed4595 100644 --- a/packages/runtime-core/src/componentPublicInstance.ts +++ b/packages/runtime-core/src/componentPublicInstance.ts @@ -1,4 +1,5 @@ import { + Component, ComponentInternalInstance, Data, getExposeProxy, @@ -33,15 +34,23 @@ import { OptionTypesKeys, resolveMergedOptions, shouldCacheAccess, - MergedComponentOptionsOverride + MergedComponentOptionsOverride, + BetterOptionTypesType, + BetterComponentOptionsBase, + BetterOptionTypesKeys, + BetterComponentOptionsMixin, + BetterComponentOptionsAny } from './componentOptions' -import { EmitsOptions, EmitFn } from './componentEmits' -import { Slots } from './componentSlots' +import { EmitsOptions, EmitFn, EmitsToProps } from './componentEmits' +import { RenderSlot, Slots } from './componentSlots' import { markAttrsAccessed } from './componentRenderUtils' import { currentRenderingInstance } from './componentRenderContext' import { warn } from './warning' import { UnionToIntersection } from './helpers/typeUtils' +import { Directive } from './directives' import { installCompatInstanceProperties } from './compat/instance' +import { Slot } from './componentSlots' +import { PublicProps } from './apiDefineComponent' /** * Custom properties added to component instances in any way and can be accessed through `this` @@ -84,24 +93,71 @@ type MixinToOptionTypes = T extends ComponentOptionsBase< infer M, infer Mixin, infer Extends, + infer E, + any, + any, + any, any, any, infer Defaults > - ? OptionTypesType

& + ? OptionTypesType

& IntersectionMixin & IntersectionMixin : never +type BetterMixinToOptionTypes = T extends BetterComponentOptionsBase< + infer Props, + infer Emits, + any, + infer LC, + infer LD, + infer B, + infer D, + infer C, + infer M, + any, + any, + infer Mixin, + infer Extends, + infer Defaults +> + ? BetterOptionTypesType< + Props & {}, + Emits /* & {}*/, + LC & {}, + LD & {}, + B & {}, + D & {}, + C & {}, + M & {}, + Defaults & {} + > & + BetterIntersectionMixin & + BetterIntersectionMixin + : never + // ExtractMixin(map type) is used to resolve circularly references type ExtractMixin = { Mixin: MixinToOptionTypes }[T extends ComponentOptionsMixin ? 'Mixin' : never] type IntersectionMixin = IsDefaultMixinComponent extends true - ? OptionTypesType<{}, {}, {}, {}, {}> + ? OptionTypesType<{}, {}, {}, {}, {}, {}, {}> : UnionToIntersection> +type BetterExtractMixin = { + Mixin: MixinToOptionTypes +}[T extends ComponentOptionsMixin ? 'Mixin' : never] +type BetterIntersectionMixin = IsDefaultMixinComponent extends true + ? BetterOptionTypesType<{}, {}, {}, {}, {}, {}, {}> + : BetterExtractMixin + +type BetterUnwrapMixinsType< + T, + Type extends BetterOptionTypesKeys +> = T extends BetterOptionTypesType ? T[Type] : never + type UnwrapMixinsType< T, Type extends OptionTypesKeys @@ -109,6 +165,67 @@ type UnwrapMixinsType< type EnsureNonVoid = T extends void ? {} : T +/** + Props extends Record = {}, + Emits extends EmitsOptions = {}, + S extends Slots = {}, + Bindings extends Record = {}, + LC extends Record = {}, + LD extends Record = {}, + D = {}, // return from data() + Options extends BetterComponentOptionsAny = BetterComponentOptionsBase< + Props, + Emits, + S, + LC, + LD, + any, + D, + any, + any + > + +*/ + +// Render component AKA ComponentPublicInstanceConstructor +// NOTE maybe this can be a class? to allow more advanced typing +export type RenderComponent< + Props extends Record = {}, + Emits extends EmitsOptions = {}, + S = {}, + // Allows to expose public API properties, this bypasses Data/Computed/Methods, + // easier to declare manually + Bindings extends Record = {}, + LC extends Record = {}, + LD extends Record = {}, + D = {}, // return from data() + Options extends BetterComponentOptionsAny = BetterComponentOptionsBase< + Props, + Emits, + S, + LC, + LD, + any, + D, + any, + any + > +> = { + __isFragment?: never + __isTeleport?: never + __isSuspense?: never + new (...args: any[]): BetterComponentPublicInstance< + Props, + Emits, + Readonly>, + Bindings, + LC, + LD, + D, + Options + > +} + export type ComponentPublicInstanceConstructor< T extends ComponentPublicInstance< Props, @@ -129,6 +246,86 @@ export type ComponentPublicInstanceConstructor< new (...args: any[]): T } +type FixS = T extends string[] + ? // the omit serves to remove the type 'bar[]' from `bar[] & { foo: Function}` + Record & + (T extends object ? Omit : never) + : T + +export type BetterCreateComponentPublicInstance< + P extends Record = {}, + E extends EmitsOptions = {}, + S = any, + LC extends Record = {}, + LD extends Record = {}, + B = {}, + D = {}, + C extends ComputedOptions = {}, + M extends MethodOptions = {}, + Exposed extends string = string, + Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, + Extends extends ComponentOptionsMixin = ComponentOptionsMixin, + PublicProps = P, + Defaults = {}, + MakeDefaultsOptional extends boolean = false, + PublicMixin = BetterIntersectionMixin & + BetterIntersectionMixin, + PublicP extends Record = Readonly< + BetterUnwrapMixinsType + > & + EnsureNonVoid

, + PublicLC extends Record = BetterUnwrapMixinsType< + PublicMixin, + 'LC' + > & + EnsureNonVoid, + PublicLD extends Record = BetterUnwrapMixinsType< + PublicMixin, + 'LD' + > & + EnsureNonVoid, + PublicB = BetterUnwrapMixinsType & EnsureNonVoid, + PublicD = BetterUnwrapMixinsType & EnsureNonVoid, + PublicC extends ComputedOptions = BetterUnwrapMixinsType & + EnsureNonVoid, + PublicM extends MethodOptions = BetterUnwrapMixinsType & + EnsureNonVoid, + // Emits behave a bit different and require union instead of intersection + PublicE extends EmitsOptions = FixS< + BetterUnwrapMixinsType, 'E'> + > & + FixS, 'E'>> & + FixS>, + PublicDefaults = BetterUnwrapMixinsType & + EnsureNonVoid +> = BetterComponentPublicInstance< + (MakeDefaultsOptional extends true + ? Partial & + Omit + : PublicP & PublicProps) & + EmitsToProps, + PublicE, + Readonly>, + ExposedKeys< + // Props + (MakeDefaultsOptional extends true + ? Partial & + Omit + : PublicP & PublicProps) & + EmitsToProps & + // /Props + + ShallowUnwrapRef & + UnwrapNestedRefs & + ExtractComputedReturns & + PublicM, + Exposed + >, + PublicLC, + PublicLD, + PublicD +> + export type CreateComponentPublicInstance< P = {}, B = {}, @@ -138,17 +335,27 @@ export type CreateComponentPublicInstance< Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = {}, + S = any, PublicProps = P, Defaults = {}, MakeDefaultsOptional extends boolean = false, + LC extends Record = {}, + Directives extends Record = {}, + Exposed extends string = string, PublicMixin = IntersectionMixin & IntersectionMixin, - PublicP = UnwrapMixinsType & EnsureNonVoid

, + PublicP = Readonly> & EnsureNonVoid

, PublicB = UnwrapMixinsType & EnsureNonVoid, PublicD = UnwrapMixinsType & EnsureNonVoid, PublicC extends ComputedOptions = UnwrapMixinsType & EnsureNonVoid, PublicM extends MethodOptions = UnwrapMixinsType & EnsureNonVoid, + // Emits behave a bit different and require union instead of intersection + PublicE extends EmitsOptions = FixS< + UnwrapMixinsType, 'E'> + > & + FixS, 'E'>> & + FixS>, PublicDefaults = UnwrapMixinsType & EnsureNonVoid > = ComponentPublicInstance< @@ -157,13 +364,78 @@ export type CreateComponentPublicInstance< PublicD, PublicC, PublicM, - E, + PublicE, + S, PublicProps, PublicDefaults, MakeDefaultsOptional, - ComponentOptionsBase + ComponentOptionsBase< + P, + B, + D, + C, + M, + Mixin, + Extends, + E, + string, + S, + LC, + Directives, + Exposed, + Defaults + >, + Exposed > +export type ExposedKeys< + T, + Exposed extends string & keyof T +> = '' extends Exposed ? T : Pick + +export type BetterComponentPublicInstance< + Props extends Record = {}, + Emits extends EmitsOptions = {}, + S extends Slots = {}, + Bindings extends Record = {}, + LC extends Record = {}, + LD extends Record = {}, + D = {}, // return from data() + Options extends BetterComponentOptionsAny = BetterComponentOptionsBase< + Props, + Emits, + S, + LC, + LD, + any, + D, + any, + any + > +> = { + $: ComponentInternalInstance + $data: D + $props: Props & PublicProps + + $attrs: Data + $refs: Data + $slots: Readonly + $root: BetterComponentPublicInstance | null + $parent: BetterComponentPublicInstance | null + $emit: EmitFn + $el: any + $options: Options & MergedComponentOptionsOverride + $forceUpdate: () => void + $nextTick: typeof nextTick + $watch( + source: string | Function, + cb: Function, + options?: WatchOptions + ): WatchStopHandle +} & Bindings & + // TODO check if the custom properties are exposed with expose + ComponentCustomProperties + // public properties exposed on the proxy, which is used as the render context // in templates (as `this` in the render option) export type ComponentPublicInstance< @@ -173,22 +445,37 @@ export type ComponentPublicInstance< C extends ComputedOptions = {}, M extends MethodOptions = {}, E extends EmitsOptions = {}, + S = any, PublicProps = P, Defaults = {}, MakeDefaultsOptional extends boolean = false, - Options = ComponentOptionsBase + Options = ComponentOptionsBase< + any, + any, + any, + any, + any, + any, + any, + any, + any, + any + >, + Exposed extends string = '' > = { + $XXX: E $: ComponentInternalInstance $data: D - $props: MakeDefaultsOptional extends true + $props: (MakeDefaultsOptional extends true ? Partial & Omit

- : P & PublicProps + : P & PublicProps) & + EmitsToProps $attrs: Data $refs: Data - $slots: Slots + $slots: Slots $root: ComponentPublicInstance | null $parent: ComponentPublicInstance | null - $emit: EmitFn + $emit: EmitFn $el: any $options: Options & MergedComponentOptionsOverride $forceUpdate: () => void @@ -198,12 +485,15 @@ export type ComponentPublicInstance< cb: Function, options?: WatchOptions ): WatchStopHandle -} & P & - ShallowUnwrapRef & - UnwrapNestedRefs & - ExtractComputedReturns & - M & - ComponentCustomProperties +} & ExposedKeys< + P & + ShallowUnwrapRef & + UnwrapNestedRefs & + ExtractComputedReturns & + M & + ComponentCustomProperties, + Exposed +> export type PublicPropertiesMap = Record< string, diff --git a/packages/runtime-core/src/componentSlots.ts b/packages/runtime-core/src/componentSlots.ts index ec58fd695c0..542bb0a524e 100644 --- a/packages/runtime-core/src/componentSlots.ts +++ b/packages/runtime-core/src/componentSlots.ts @@ -22,16 +22,37 @@ import { isHmrUpdating } from './hmr' import { DeprecationTypes, isCompatEnabled } from './compat/compatConfig' import { toRaw } from '@vue/reactivity' -export type Slot = (...args: any[]) => VNode[] +export type Slot = (...args: any[]) => VNode[] | VNode -export type InternalSlots = { - [name: string]: Slot | undefined +export type SlotTyped = T extends null + ? () => VNode[] | VNode + : (arg: T) => VNode[] | VNode + +export type InternalSlots = { + [K in keyof T]?: T[K] extends () => infer R ? SlotTyped : SlotTyped } -export type Slots = Readonly +export type SlotsObject = InternalSlots +export type SlotArray = V extends PropertyKey + ? Record + : Record -export type RawSlots = { - [name: string]: unknown +export type Slots = RenderSlot & + (unknown extends T + ? Readonly>> + : T extends Array + ? Readonly> + : Readonly>) + +declare const rr: Slots<{ a: null }> extends Readonly> + ? true + : false + +if (rr) { + rr?.valueOf() +} + +export type RenderSlot = { // manual render fn hint to skip forced children updates $stable?: boolean /** @@ -50,6 +71,9 @@ export type RawSlots = { */ _?: SlotFlags } +export type RawSlots = { + [name: string]: unknown +} & RenderSlot const isInternalKey = (key: string) => key[0] === '_' || key === '$stable' diff --git a/packages/runtime-core/src/directives.ts b/packages/runtime-core/src/directives.ts index 58e56df0522..4b834879221 100644 --- a/packages/runtime-core/src/directives.ts +++ b/packages/runtime-core/src/directives.ts @@ -22,19 +22,29 @@ import { mapCompatDirectiveHook } from './compat/customDirective' import { pauseTracking, resetTracking } from '@vue/reactivity' import { traverse } from './apiWatch' -export interface DirectiveBinding { +export interface DirectiveBinding< + Value = any, + Modifiers extends string = string, + Arg extends string = string +> { instance: ComponentPublicInstance | null - value: V - oldValue: V | null - arg?: string - modifiers: DirectiveModifiers - dir: ObjectDirective + value: Value + oldValue: Value | null + arg?: Arg + modifiers: DirectiveModifiers + dir: ObjectDirective } -export type DirectiveHook | null, V = any> = ( - el: T, - binding: DirectiveBinding, - vnode: VNode, +export type DirectiveHook< + HostElement = any, + Prev = VNode | null, + Value = any, + Modifiers extends string = string, + Arg extends string = string +> = ( + el: HostElement, + binding: DirectiveBinding, + vnode: VNode, prevVNode: Prev ) => void @@ -43,25 +53,52 @@ export type SSRDirectiveHook = ( vnode: VNode ) => Data | undefined -export interface ObjectDirective { - created?: DirectiveHook - beforeMount?: DirectiveHook - mounted?: DirectiveHook - beforeUpdate?: DirectiveHook, V> - updated?: DirectiveHook, V> - beforeUnmount?: DirectiveHook - unmounted?: DirectiveHook +export interface ObjectDirective< + HostElement = any, + Value = any, + Modifiers extends string = string, + Arg extends string = string +> { + created?: DirectiveHook + beforeMount?: DirectiveHook + mounted?: DirectiveHook + beforeUpdate?: DirectiveHook< + HostElement, + VNode, + Value, + Modifiers, + Arg + > + updated?: DirectiveHook< + HostElement, + VNode, + Value, + Modifiers, + Arg + > + beforeUnmount?: DirectiveHook + unmounted?: DirectiveHook getSSRProps?: SSRDirectiveHook deep?: boolean } -export type FunctionDirective = DirectiveHook +export type FunctionDirective< + HostElement = any, + V = any, + Modifiers extends string = string, + Arg extends string = string +> = DirectiveHook -export type Directive = - | ObjectDirective - | FunctionDirective +export type Directive< + HostElement = any, + Value = any, + Modifiers extends string = string, + Arg extends string = string +> = + | ObjectDirective + | FunctionDirective -export type DirectiveModifiers = Record +export type DirectiveModifiers = Record const isBuiltInDirective = /*#__PURE__*/ makeMap( 'bind,cloak,else-if,else,for,html,if,model,on,once,pre,show,slot,text' diff --git a/packages/runtime-core/src/h.ts b/packages/runtime-core/src/h.ts index f22e4bb30d0..753503ec367 100644 --- a/packages/runtime-core/src/h.ts +++ b/packages/runtime-core/src/h.ts @@ -15,11 +15,15 @@ import { RawSlots } from './componentSlots' import { FunctionalComponent, Component, + ConcreteComponent, ComponentOptions, - ConcreteComponent + BetterComponent } from './component' import { EmitsOptions } from './componentEmits' -import { DefineComponent } from './apiDefineComponent' +import { betterDefineComponent, DefineComponent } from './apiDefineComponent' +import { Prop, Slots } from 'test-dts' +import { BetterComponentOptions } from './componentOptions' +import { RenderComponent } from './componentPublicInstance' // `h` is a more user-friendly version of `createVNode` that allows omitting the // props when possible. It is intended for manually written render functions. @@ -68,16 +72,67 @@ type RawChildren = | (() => any) // fake constructor type returned from `defineComponent` -interface Constructor

{ +interface Constructor

{ __isFragment?: never __isTeleport?: never __isSuspense?: never - new (...args: any[]): { $props: P } + __isDefineComponent?: never + new (...args: any[]): { $props: P; $slots?: S } } +// Converts emits value to object +type ExtractEmitEvents = T extends Readonly> + ? { [K in V & string as `on${Capitalize}`]: (...args: any[]) => void } + : T extends any[] + ? { [K in T & string as `on${Capitalize}`]: (...args: any[]) => void } + : {} extends T // if the emit is empty object (usually the default value for emit) should be converted to function + ? {} + : { + [K in keyof T & string as `on${Capitalize}`]: T[K] extends ( + ...args: infer Args + ) => any + ? (...args: Args) => void + : (...args: any[]) => void + } + +// TODO remove `on*` props aka Emit events +type ExtractEmitPropUpdate< + P = {}, + PK extends keyof P & string = keyof P & string +> = P extends Readonly> + ? { [K in V & string as `onUpdate:${K}`]?: (value: any) => void } + : P extends any[] + ? { [K in P & string as `onUpdate:${K}`]?: (value: any) => void } + : // we need to omit if it infers emit as props + { + [K in keyof Omit}`> & + string as `onUpdate:${K}`]?: (value: P[K]) => void + } + +type RenderProps = + | (Partial> & RawProps & P & ExtractEmitPropUpdate

) + | ({} extends P ? Partial> | null : never) + +type RenderSlots = + | Slots + | ({} extends S ? RawSlots : Slots) + | RawChildren + // The following is a series of overloads for providing props validation of // manually written render functions. +// functional component +// NOTE: is set on top to allow infer the props when doing +/// const Func = (_props: { foo: string; bar?: number }) => '' +/// h(Func, {}) +// otherwise it will default to `h(type: string)` +export function h( + type: FunctionalComponent, + props?: RenderProps, + children?: RawChildren | RawSlots +): VNode +export function h(type: FunctionalComponent): VNode + // element export function h(type: string, children?: RawChildren): VNode export function h( @@ -120,23 +175,24 @@ export function h( ): VNode // functional component -export function h( - type: FunctionalComponent, - props?: (RawProps & P) | ({} extends P ? null : never), - children?: RawChildren | RawSlots -): VNode +// export function h( +// type: FunctionalComponent, +// props?: (RawProps & P) | ({} extends P ? null : never), +// children?: RawChildren | RawSlots +// ): VNode // catch-all for generic component types export function h(type: Component, children?: RawChildren): VNode // concrete component -export function h

( - type: ConcreteComponent | string, +export function h( + type: ConcreteComponent | string, + props?: RenderProps, children?: RawChildren ): VNode + export function h

( - type: ConcreteComponent

| string, - props?: (RawProps & P) | ({} extends P ? null : never), + type: ConcreteComponent | string, children?: RawChildren ): VNode @@ -148,27 +204,59 @@ export function h( ): VNode // exclude `defineComponent` constructors -export function h

( - type: ComponentOptions

, - props?: (RawProps & P) | ({} extends P ? null : never), - children?: RawChildren | RawSlots -): VNode - -// fake constructor type returned by `defineComponent` or class component -export function h(type: Constructor, children?: RawChildren): VNode -export function h

( - type: Constructor

, - props?: (RawProps & P) | ({} extends P ? null : never), - children?: RawChildren | RawSlots +export function h< + P, + S, + E extends EmitsOptions = {}, + PP = {}, + Props = {}, + Defaults = {} +>( + type: ComponentOptions, + props?: RenderProps & Omit, E>, + children?: (RawChildren & Slots) | ({} extends S ? RawSlots : Slots) ): VNode // fake constructor type returned by `defineComponent` +export function h< + P, + S, + E extends EmitsOptions = {}, + PP = {}, + Props = {}, + Defaults = {} +>( + type: DefineComponent< + P, + any, + any, + any, + any, + any, + any, + E, + any, + S, + {}, + {}, + string, + PP, + Props, + Defaults + >, + props?: RenderProps & Omit, E>, + children?: (RawChildren & Slots) | ({} extends S ? RawSlots : Slots) +): VNode +export function h(type: DefineComponent): VNode export function h(type: DefineComponent, children?: RawChildren): VNode -export function h

( - type: DefineComponent

, - props?: (RawProps & P) | ({} extends P ? null : never), - children?: RawChildren | RawSlots + +// fake constructor type returned by `defineComponent` or class component +export function h( + type: Constructor, + props?: RenderProps, + children?: (RawChildren & Slots) | ({} extends S ? RawSlots : Slots) ): VNode +export function h(type: Constructor, children?: RawChildren): VNode // Actual implementation export function h(type: any, propsOrChildren?: any, children?: any): VNode { @@ -194,3 +282,101 @@ export function h(type: any, propsOrChildren?: any, children?: any): VNode { return createVNode(type, propsOrChildren, children) } } + +// declare function betterH< +// P extends Record, +// E extends EmitsOptions, +// S +// >( +// type: BetterComponentOptions, +// props?: RenderProps, +// children?: (RawChildren & Slots) | ({} extends S ? RawSlots : Slots) +// ): P +export function betterH< + P extends Record, + E extends EmitsOptions, + S +>( + type: BetterComponent, + props?: RenderProps, + children?: RenderSlots +): P + +export function betterH< + P extends Record, + E extends EmitsOptions, + S +>( + type: RenderComponent, + props?: RenderProps, + children?: RenderSlots +): P +export function betterH( + type: any, + propsOrChildren?: any, + children?: any +): any {} + +declare const MyComp: BetterComponent< + { test: number }, + ['hey'], + { + default: null + typedSlot: { a: number } + } +> +const r = betterH( + MyComp, + { + test: 2, + onHey(rrr) {} + }, + { + default() { + return {} as unknown as VNode + }, + typedSlot(e) { + return {} as unknown as VNode + } + } +) + +const x = betterH( + {} as unknown as RenderComponent< + { + test: number + }, + ['tst'], + { + default: null + } + >, + { + test: 2 + } +) + +const Comp = betterDefineComponent({ + // props: ['test'] +}) + +betterH( + Comp, + { + test: 2 + }, + {} +) + +declare function test(t: BetterComponent): boolean +test(Comp) + +declare const MyComp2: Component<{ test: number }> + +h( + MyComp2, + { + test: 2 + }, + {} +) diff --git a/packages/runtime-core/src/helpers/typeUtils.ts b/packages/runtime-core/src/helpers/typeUtils.ts index 204543e6de2..8ab2862afd6 100644 --- a/packages/runtime-core/src/helpers/typeUtils.ts +++ b/packages/runtime-core/src/helpers/typeUtils.ts @@ -5,4 +5,6 @@ export type UnionToIntersection = ( : never // make keys required but keep undefined values -export type LooseRequired = { [P in string & keyof T]: T[P] } +// export type LooseRequired = { [P in string & keyof T]: T[P] } +// TODO validate this change, was what's above +export type LooseRequired = { [P in keyof T]: T[P] } diff --git a/packages/runtime-core/src/index.ts b/packages/runtime-core/src/index.ts index c46b6df2276..b2fc21d3341 100644 --- a/packages/runtime-core/src/index.ts +++ b/packages/runtime-core/src/index.ts @@ -146,6 +146,23 @@ declare module '@vue/reactivity' { } } +// Augment GlobalComponents +// Note: if updating this, also update `types/globalComponents.d.ts`. +import { TeleportProps } from './components/Teleport' +import { SuspenseProps } from './components/Suspense' +import { KeepAliveProps } from './components/KeepAlive' +import { BaseTransitionProps } from './components/BaseTransition' +import { DefineComponent } from './apiDefineComponent' + +declare module '@vue/runtime-core' { + export interface GlobalComponents { + Teleport: DefineComponent + Suspense: DefineComponent + KeepAlive: DefineComponent + BaseTransition: DefineComponent + } +} + export { ReactiveEffectOptions, DebuggerEvent, @@ -192,7 +209,9 @@ export { ComponentInternalInstance, SetupContext, ComponentCustomProps, - AllowedComponentProps + AllowedComponentProps, + GlobalDirectives, + GlobalComponents } from './component' export { DefineComponent } from './apiDefineComponent' export { diff --git a/packages/runtime-core/src/scheduler.ts b/packages/runtime-core/src/scheduler.ts index 333125d578d..39b8d6030c7 100644 --- a/packages/runtime-core/src/scheduler.ts +++ b/packages/runtime-core/src/scheduler.ts @@ -128,10 +128,7 @@ function queueCb( if (!isArray(cb)) { if ( !activeQueue || - !activeQueue.includes( - cb, - cb.allowRecurse ? index + 1 : index - ) + !activeQueue.includes(cb, cb.allowRecurse ? index + 1 : index) ) { pendingQueue.push(cb) } diff --git a/packages/runtime-core/types/globalComponents.d.ts b/packages/runtime-core/types/globalComponents.d.ts new file mode 100644 index 00000000000..a4abd6d1fff --- /dev/null +++ b/packages/runtime-core/types/globalComponents.d.ts @@ -0,0 +1,11 @@ +// Note: this file is auto concatenated to the end of the bundled d.ts during +// build. + +declare module '@vue/runtime-core' { + export interface GlobalComponents { + Teleport: DefineComponent + Suspense: DefineComponent + KeepAlive: DefineComponent + BaseTransition: DefineComponent + } +} diff --git a/packages/runtime-dom/__tests__/directives/vOn.spec.ts b/packages/runtime-dom/__tests__/directives/vOn.spec.ts index 477620f6da6..fd64dda9d9a 100644 --- a/packages/runtime-dom/__tests__/directives/vOn.spec.ts +++ b/packages/runtime-dom/__tests__/directives/vOn.spec.ts @@ -41,7 +41,7 @@ describe('runtime-dom: v-on directive', () => { }) test('it should support key modifiers and system modifiers', () => { - const keyNames = ['ctrl', 'shift', 'meta', 'alt'] + const keyNames = ['ctrl', 'shift', 'meta', 'alt'] as const keyNames.forEach(keyName => { const el = document.createElement('div') diff --git a/packages/runtime-dom/src/directives/vModel.ts b/packages/runtime-dom/src/directives/vModel.ts index 11fd5376055..8d0677094f9 100644 --- a/packages/runtime-dom/src/directives/vModel.ts +++ b/packages/runtime-dom/src/directives/vModel.ts @@ -40,12 +40,17 @@ function trigger(el: HTMLElement, type: string) { el.dispatchEvent(e) } -type ModelDirective = ObjectDirective +type ModelDirective = ObjectDirective< + T & { _assign: AssignerFn }, + any, + Modifiers +> // We are exporting the v-model runtime directly as vnode hooks so that it can // be tree-shaken in case v-model is never used. export const vModelText: ModelDirective< - HTMLInputElement | HTMLTextAreaElement + HTMLInputElement | HTMLTextAreaElement, + 'trim' | 'number' | 'lazy' > = { created(el, { modifiers: { lazy, trim, number } }, vnode) { el._assign = getModelAssigner(vnode) @@ -176,7 +181,7 @@ export const vModelRadio: ModelDirective = { } } -export const vModelSelect: ModelDirective = { +export const vModelSelect: ModelDirective = { //