]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
types(defineComponent): support for GlobalComponents, typed Directives and respect...
authorCarlos Rodrigues <carlos@hypermob.co.uk>
Thu, 25 Apr 2024 08:04:03 +0000 (09:04 +0100)
committerGitHub <noreply@github.com>
Thu, 25 Apr 2024 08:04:03 +0000 (16:04 +0800)
close #3367

15 files changed:
packages/dts-test/componentTypeExtensions.test-d.tsx
packages/dts-test/defineComponent.test-d.tsx
packages/dts-test/directives.test-d.ts [new file with mode: 0644]
packages/runtime-core/src/apiDefineComponent.ts
packages/runtime-core/src/component.ts
packages/runtime-core/src/componentOptions.ts
packages/runtime-core/src/componentPublicInstance.ts
packages/runtime-core/src/directives.ts
packages/runtime-core/src/index.ts
packages/runtime-core/types/globalComponents.d.ts [new file with mode: 0644]
packages/runtime-dom/__tests__/directives/vOn.spec.ts
packages/runtime-dom/src/directives/vModel.ts
packages/runtime-dom/src/directives/vOn.ts
packages/runtime-dom/src/index.ts
packages/vue-compat/__tests__/instance.spec.ts

index 68364191945aa8b873c23283b3992c908bc523b3..fe58c0a6449f3c1fadc809ba8b84fa57e007e79b 100644 (file)
@@ -1,4 +1,4 @@
-import { defineComponent } from 'vue'
+import { type DefineComponent, type Directive, defineComponent } from 'vue'
 import { expectType } from './utils'
 
 declare module 'vue' {
@@ -6,6 +6,14 @@ declare module 'vue' {
     test?(n: number): void
   }
 
+  interface GlobalDirectives {
+    test: Directive
+  }
+
+  interface GlobalComponents {
+    RouterView: DefineComponent<{}>
+  }
+
   interface ComponentCustomProperties {
     state?: 'stopped' | 'running'
   }
@@ -46,6 +54,8 @@ export const Custom = defineComponent({
   },
 })
 
+expectType<Directive>(Custom.directives!.test)
+expectType<DefineComponent<{}>>(Custom.components!.RouterView)
 expectType<JSX.Element>(<Custom baz={1} />)
 expectType<JSX.Element>(<Custom custom={1} baz={1} />)
 expectType<JSX.Element>(<Custom bar="bar" baz={1} />)
index 41646751b8b78999fdb78c8af23a86d7fe82cd1c..44a00d4e586efe9c885f78b3db879607a7f33020 100644 (file)
@@ -1501,18 +1501,108 @@ describe('should work when props type is incompatible with setup returned type '
 
 describe('withKeys and withModifiers as pro', () => {
   const onKeydown = withKeys(e => {}, [''])
+  // @ts-expect-error invalid modifiers
   const onClick = withModifiers(e => {}, [''])
   ;<input onKeydown={onKeydown} onClick={onClick} />
 })
 
+// #3367 expose components types
+describe('expose component types', () => {
+  const child = defineComponent({
+    props: {
+      a: String,
+    },
+  })
+
+  const parent = defineComponent({
+    components: {
+      child,
+      child2: {
+        template: `<div></div>`,
+      },
+    },
+  })
+
+  expectType<typeof child>(parent.components!.child)
+  expectType<Component>(parent.components!.child2)
+
+  // global components
+  expectType<Readonly<KeepAliveProps>>(
+    new parent.components!.KeepAlive().$props,
+  )
+  expectType<Readonly<KeepAliveProps>>(new child.components!.KeepAlive().$props)
+
+  // runtime-dom components
+  expectType<Readonly<TransitionProps>>(
+    new parent.components!.Transition().$props,
+  )
+  expectType<Readonly<TransitionProps>>(
+    new child.components!.Transition().$props,
+  )
+})
+
+describe('directive typing', () => {
+  const customDirective: Directive = {
+    created(_) {},
+  }
+
+  const comp = defineComponent({
+    props: {
+      a: String,
+    },
+    directives: {
+      customDirective,
+      localDirective: {
+        created(_, { arg }) {
+          expectType<string | undefined>(arg)
+        },
+      },
+    },
+  })
+
+  expectType<typeof customDirective>(comp.directives!.customDirective)
+  expectType<Directive>(comp.directives!.localDirective)
+
+  // global directive
+  expectType<typeof vShow>(comp.directives!.vShow)
+})
+
+describe('expose typing', () => {
+  const Comp = defineComponent({
+    expose: ['a', 'b'],
+    props: {
+      some: String,
+    },
+    data() {
+      return { a: 1, b: '2', c: 1 }
+    },
+  })
+
+  expectType<Array<'a' | 'b'>>(Comp.expose!)
+
+  const vm = new Comp()
+  // internal should still be exposed
+  vm.$props
+
+  expectType<number>(vm.a)
+  expectType<string>(vm.b)
+
+  // @ts-expect-error shouldn't be exposed
+  vm.c
+})
+
 import type {
   AllowedComponentProps,
   ComponentCustomProps,
   ComponentOptionsMixin,
   DefineComponent,
+  Directive,
   EmitsOptions,
   ExtractPropTypes,
+  KeepAliveProps,
+  TransitionProps,
   VNodeProps,
+  vShow,
 } from 'vue'
 
 // code generated by tsc / vue-tsc, make sure this continues to work
diff --git a/packages/dts-test/directives.test-d.ts b/packages/dts-test/directives.test-d.ts
new file mode 100644 (file)
index 0000000..5b87ebf
--- /dev/null
@@ -0,0 +1,58 @@
+import { type Directive, type ObjectDirective, vModelText } from 'vue'
+import { describe, expectType } from './utils'
+
+type ExtractBinding<T> = T extends (
+  el: any,
+  binding: infer B,
+  vnode: any,
+  prev: any,
+) => any
+  ? B
+  : never
+
+declare function testDirective<
+  Value,
+  Modifiers extends string = string,
+  Arg extends string = string,
+>(): ExtractBinding<Directive<any, Value, Modifiers, Arg>>
+
+describe('vmodel', () => {
+  expectType<ObjectDirective<any, any, 'trim' | 'number' | 'lazy', string>>(
+    vModelText,
+  )
+  // @ts-expect-error
+  expectType<ObjectDirective<any, any, 'not-valid', string>>(vModelText)
+})
+
+describe('custom', () => {
+  expectType<{
+    value: number
+    oldValue: number | null
+    arg?: 'Arg'
+    modifiers: Record<'a' | 'b', boolean>
+  }>(testDirective<number, 'a' | 'b', 'Arg'>())
+
+  expectType<{
+    value: number
+    oldValue: number | null
+    arg?: 'Arg'
+    modifiers: Record<'a' | 'b', boolean>
+    // @ts-expect-error
+  }>(testDirective<number, 'a', 'Arg'>())
+
+  expectType<{
+    value: number
+    oldValue: number | null
+    arg?: 'Arg'
+    modifiers: Record<'a' | 'b', boolean>
+    // @ts-expect-error
+  }>(testDirective<number, 'a' | 'b', 'Argx'>())
+
+  expectType<{
+    value: number
+    oldValue: number | null
+    arg?: 'Arg'
+    modifiers: Record<'a' | 'b', boolean>
+    // @ts-expect-error
+  }>(testDirective<string, 'a' | 'b', 'Arg'>())
+})
index 8c0492e1cef9da606df7c48d5dc2fa700c288226..46e1d59a8e7328d6de9561cc04f7aafd6304b8f2 100644 (file)
@@ -6,13 +6,17 @@ import type {
   ComponentOptionsWithArrayProps,
   ComponentOptionsWithObjectProps,
   ComponentOptionsWithoutProps,
+  ComponentProvideOptions,
   ComputedOptions,
   MethodOptions,
   RenderFunction,
 } from './componentOptions'
 import type {
   AllowedComponentProps,
+  Component,
   ComponentCustomProps,
+  GlobalComponents,
+  GlobalDirectives,
   SetupContext,
 } from './component'
 import type {
@@ -29,6 +33,7 @@ import type {
   CreateComponentPublicInstance,
 } from './componentPublicInstance'
 import type { SlotsType } from './componentSlots'
+import type { Directive } from './directives'
 
 export type PublicProps = VNodeProps &
   AllowedComponentProps &
@@ -55,6 +60,10 @@ export type DefineComponent<
   Props = ResolveProps<PropsOrPropOptions, E>,
   Defaults = ExtractDefaultPropTypes<PropsOrPropOptions>,
   S extends SlotsType = {},
+  LC extends Record<string, Component> = {},
+  Directives extends Record<string, Directive> = {},
+  Exposed extends string = string,
+  Provide extends ComponentProvideOptions = ComponentProvideOptions,
 > = ComponentPublicInstanceConstructor<
   CreateComponentPublicInstance<
     Props,
@@ -69,7 +78,10 @@ export type DefineComponent<
     Defaults,
     true,
     {},
-    S
+    S,
+    LC & GlobalComponents,
+    Directives & GlobalDirectives,
+    Exposed
   >
 > &
   ComponentOptionsBase<
@@ -85,7 +97,11 @@ export type DefineComponent<
     Defaults,
     {},
     string,
-    S
+    S,
+    LC & GlobalComponents,
+    Directives & GlobalDirectives,
+    Exposed,
+    Provide
   > &
   PP
 
@@ -166,9 +182,13 @@ export function defineComponent<
   Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
   E extends EmitsOptions = {},
   EE extends string = string,
-  S extends SlotsType = {},
   I extends ComponentInjectOptions = {},
   II extends string = string,
+  S extends SlotsType = {},
+  LC extends Record<string, Component> = {},
+  Directives extends Record<string, Directive> = {},
+  Exposed extends string = string,
+  Provide extends ComponentProvideOptions = ComponentProvideOptions,
 >(
   options: ComponentOptionsWithoutProps<
     Props,
@@ -182,7 +202,11 @@ export function defineComponent<
     EE,
     I,
     II,
-    S
+    S,
+    LC,
+    Directives,
+    Exposed,
+    Provide
   >,
 ): DefineComponent<
   Props,
@@ -197,7 +221,11 @@ export function defineComponent<
   PublicProps,
   ResolveProps<Props, E>,
   ExtractDefaultPropTypes<Props>,
-  S
+  S,
+  LC,
+  Directives,
+  Exposed,
+  Provide
 >
 
 // overload 3: object format with array props declaration
@@ -216,6 +244,10 @@ export function defineComponent<
   S extends SlotsType = {},
   I extends ComponentInjectOptions = {},
   II extends string = string,
+  LC extends Record<string, Component> = {},
+  Directives extends Record<string, Directive> = {},
+  Exposed extends string = string,
+  Provide extends ComponentProvideOptions = ComponentProvideOptions,
   Props = Readonly<{ [key in PropNames]?: any }>,
 >(
   options: ComponentOptionsWithArrayProps<
@@ -230,7 +262,11 @@ export function defineComponent<
     EE,
     I,
     II,
-    S
+    S,
+    LC,
+    Directives,
+    Exposed,
+    Provide
   >,
 ): DefineComponent<
   Props,
@@ -245,7 +281,11 @@ export function defineComponent<
   PublicProps,
   ResolveProps<Props, E>,
   ExtractDefaultPropTypes<Props>,
-  S
+  S,
+  LC,
+  Directives,
+  Exposed,
+  Provide
 >
 
 // overload 4: object format with object props declaration
@@ -262,9 +302,13 @@ export function defineComponent<
   Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
   E extends EmitsOptions = {},
   EE extends string = string,
-  S extends SlotsType = {},
   I extends ComponentInjectOptions = {},
   II extends string = string,
+  S extends SlotsType = {},
+  LC extends Record<string, Component> = {},
+  Directives extends Record<string, Directive> = {},
+  Exposed extends string = string,
+  Provide extends ComponentProvideOptions = ComponentProvideOptions,
 >(
   options: ComponentOptionsWithObjectProps<
     PropsOptions,
@@ -278,7 +322,11 @@ export function defineComponent<
     EE,
     I,
     II,
-    S
+    S,
+    LC,
+    Directives,
+    Exposed,
+    Provide
   >,
 ): DefineComponent<
   PropsOptions,
@@ -293,7 +341,11 @@ export function defineComponent<
   PublicProps,
   ResolveProps<PropsOptions, E>,
   ExtractDefaultPropTypes<PropsOptions>,
-  S
+  S,
+  LC,
+  Directives,
+  Exposed,
+  Provide
 >
 
 // implementation, close to no-op
index b8466923a921051b4d43023f8bc81d317f8a0fbb..68d908310d434fb3ef473551b7e00f9fbaab4f57 100644 (file)
@@ -86,6 +86,13 @@ import {
 import type { SchedulerJob } from './scheduler'
 import type { LifecycleHooks } from './enums'
 
+// Augment GlobalComponents
+import type { TeleportProps } from './components/Teleport'
+import type { SuspenseProps } from './components/Suspense'
+import type { KeepAliveProps } from './components/KeepAlive'
+import type { BaseTransitionProps } from './components/BaseTransition'
+import type { DefineComponent } from './apiDefineComponent'
+
 export type Data = Record<string, unknown>
 
 /**
@@ -126,6 +133,45 @@ export type ComponentInstance<T> = T extends { new (): ComponentPublicInstance }
  */
 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<string, Directive> {}
+
+/**
+ * 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<string, Component> {
+  Teleport: DefineComponent<TeleportProps>
+  Suspense: DefineComponent<SuspenseProps>
+  KeepAlive: DefineComponent<KeepAliveProps>
+  BaseTransition: DefineComponent<BaseTransitionProps>
+}
+
 /**
  * Default allowed non-declared props on component in TSX
  */
index 65b952b0b51ea84c9dc6739b2edb086e684b840a..da1dfcec0cc6f3ccd28a609a9a28b5937f0f2a7e 100644 (file)
@@ -112,7 +112,11 @@ export interface ComponentOptionsBase<
   I extends ComponentInjectOptions = {},
   II extends string = string,
   S extends SlotsType = {},
-> extends LegacyOptions<Props, D, C, M, Mixin, Extends, I, II>,
+  LC extends Record<string, Component> = {},
+  Directives extends Record<string, Directive> = {},
+  Exposed extends string = string,
+  Provide extends ComponentProvideOptions = ComponentProvideOptions,
+> extends LegacyOptions<Props, D, C, M, Mixin, Extends, I, II, Provide>,
     ComponentInternalOptions,
     ComponentCustomOptions {
   setup?: (
@@ -136,13 +140,16 @@ export interface ComponentOptionsBase<
   // Luckily `render()` doesn't need any arguments nor does it care about return
   // type.
   render?: Function
-  components?: Record<string, Component>
-  directives?: Record<string, Directive>
+  // NOTE: extending both LC and Record<string, Component> allows objects to be forced
+  // to be of type Component, while still inferring LC generic
+  components?: LC & Record<string, Component>
+  // NOTE: extending both Directives and Record<string, Directive> allows objects to be forced
+  // to be of type Directive, while still inferring Directives generic
+  directives?: Directives & Record<string, Directive>
   inheritAttrs?: boolean
   emits?: (E | EE[]) & ThisType<void>
   slots?: S
-  // TODO infer public instance type based on exposed keys
-  expose?: string[]
+  expose?: Exposed[]
   serverPrefetch?(): void | Promise<any>
 
   // Runtime compiler only -----------------------------------------------------
@@ -224,6 +231,10 @@ export type ComponentOptionsWithoutProps<
   I extends ComponentInjectOptions = {},
   II extends string = string,
   S extends SlotsType = {},
+  LC extends Record<string, Component> = {},
+  Directives extends Record<string, Directive> = {},
+  Exposed extends string = string,
+  Provide extends ComponentProvideOptions = ComponentProvideOptions,
   PE = Props & EmitsToProps<E>,
 > = ComponentOptionsBase<
   PE,
@@ -238,7 +249,11 @@ export type ComponentOptionsWithoutProps<
   {},
   I,
   II,
-  S
+  S,
+  LC,
+  Directives,
+  Exposed,
+  Provide
 > & {
   props?: undefined
 } & ThisType<
@@ -255,7 +270,10 @@ export type ComponentOptionsWithoutProps<
       {},
       false,
       I,
-      S
+      S,
+      LC,
+      Directives,
+      Exposed
     >
   >
 
@@ -272,6 +290,10 @@ export type ComponentOptionsWithArrayProps<
   I extends ComponentInjectOptions = {},
   II extends string = string,
   S extends SlotsType = {},
+  LC extends Record<string, Component> = {},
+  Directives extends Record<string, Directive> = {},
+  Exposed extends string = string,
+  Provide extends ComponentProvideOptions = ComponentProvideOptions,
   Props = Prettify<Readonly<{ [key in PropNames]?: any } & EmitsToProps<E>>>,
 > = ComponentOptionsBase<
   Props,
@@ -286,7 +308,11 @@ export type ComponentOptionsWithArrayProps<
   {},
   I,
   II,
-  S
+  S,
+  LC,
+  Directives,
+  Exposed,
+  Provide
 > & {
   props: PropNames[]
 } & ThisType<
@@ -303,7 +329,10 @@ export type ComponentOptionsWithArrayProps<
       {},
       false,
       I,
-      S
+      S,
+      LC,
+      Directives,
+      Exposed
     >
   >
 
@@ -320,6 +349,10 @@ export type ComponentOptionsWithObjectProps<
   I extends ComponentInjectOptions = {},
   II extends string = string,
   S extends SlotsType = {},
+  LC extends Record<string, Component> = {},
+  Directives extends Record<string, Directive> = {},
+  Exposed extends string = string,
+  Provide extends ComponentProvideOptions = ComponentProvideOptions,
   Props = Prettify<Readonly<ExtractPropTypes<PropsOptions> & EmitsToProps<E>>>,
   Defaults = ExtractDefaultPropTypes<PropsOptions>,
 > = ComponentOptionsBase<
@@ -335,7 +368,11 @@ export type ComponentOptionsWithObjectProps<
   Defaults,
   I,
   II,
-  S
+  S,
+  LC,
+  Directives,
+  Exposed,
+  Provide
 > & {
   props: PropsOptions & ThisType<void>
 } & ThisType<
@@ -352,7 +389,9 @@ export type ComponentOptionsWithObjectProps<
       Defaults,
       false,
       I,
-      S
+      S,
+      LC,
+      Directives
     >
   >
 
@@ -365,7 +404,15 @@ export type ComponentOptions<
   Mixin extends ComponentOptionsMixin = any,
   Extends extends ComponentOptionsMixin = any,
   E extends EmitsOptions = any,
-  S extends SlotsType = any,
+  EE extends string = string,
+  Defaults = {},
+  I extends ComponentInjectOptions = {},
+  II extends string = string,
+  S extends SlotsType = {},
+  LC extends Record<string, Component> = {},
+  Directives extends Record<string, Directive> = {},
+  Exposed extends string = string,
+  Provide extends ComponentProvideOptions = ComponentProvideOptions,
 > = ComponentOptionsBase<
   Props,
   RawBindings,
@@ -375,8 +422,15 @@ export type ComponentOptions<
   Mixin,
   Extends,
   E,
-  string,
-  S
+  EE,
+  Defaults,
+  I,
+  II,
+  S,
+  LC,
+  Directives,
+  Exposed,
+  Provide
 > &
   ThisType<
     CreateComponentPublicInstance<
@@ -388,7 +442,13 @@ export type ComponentOptions<
       Mixin,
       Extends,
       E,
-      Readonly<Props>
+      Readonly<Props>,
+      Defaults,
+      false,
+      I,
+      S,
+      LC,
+      Directives
     >
   >
 
@@ -403,6 +463,12 @@ export type ComponentOptionsMixin = ComponentOptionsBase<
   any,
   any,
   any,
+  any,
+  any,
+  any,
+  any,
+  any,
+  any,
   any
 >
 
@@ -464,6 +530,7 @@ interface LegacyOptions<
   Extends extends ComponentOptionsMixin,
   I extends ComponentInjectOptions,
   II extends string,
+  Provide extends ComponentProvideOptions = ComponentProvideOptions,
 > {
   compatConfig?: CompatConfig
 
@@ -497,7 +564,7 @@ interface LegacyOptions<
   computed?: C
   methods?: M
   watch?: ComponentWatchOptions
-  provide?: ComponentProvideOptions
+  provide?: Provide
   inject?: I | II[]
 
   // assets
index deb9ff69bc39e65c79ab0fc83dce984cdecd33b7..864b9786efe7c7eab3c965f8571a42a2a022d991 100644 (file)
@@ -1,4 +1,5 @@
 import {
+  type Component,
   type ComponentInternalInstance,
   type Data,
   getExposeProxy,
@@ -35,6 +36,7 @@ import {
   type ComponentInjectOptions,
   type ComponentOptionsBase,
   type ComponentOptionsMixin,
+  type ComponentProvideOptions,
   type ComputedOptions,
   type ExtractComputedReturns,
   type InjectToObject,
@@ -51,6 +53,7 @@ import { markAttrsAccessed } from './componentRenderUtils'
 import { currentRenderingInstance } from './componentRenderContext'
 import { warn } from './warning'
 import { installCompatInstanceProperties } from './compat/instance'
+import type { Directive } from './directives'
 
 /**
  * Custom properties added to component instances in any way and can be accessed through `this`
@@ -99,6 +102,10 @@ type MixinToOptionTypes<T> =
     infer Defaults,
     any,
     any,
+    any,
+    any,
+    any,
+    any,
     any
   >
     ? OptionTypesType<P & {}, B & {}, D & {}, C & {}, M & {}, Defaults & {}> &
@@ -157,6 +164,9 @@ export type CreateComponentPublicInstance<
   MakeDefaultsOptional extends boolean = false,
   I extends ComponentInjectOptions = {},
   S extends SlotsType = {},
+  LC extends Record<string, Component> = {},
+  Directives extends Record<string, Directive> = {},
+  Exposed extends string = string,
   PublicMixin = IntersectionMixin<Mixin> & IntersectionMixin<Extends>,
   PublicP = UnwrapMixinsType<PublicMixin, 'P'> & EnsureNonVoid<P>,
   PublicB = UnwrapMixinsType<PublicMixin, 'B'> & EnsureNonVoid<B>,
@@ -167,6 +177,7 @@ export type CreateComponentPublicInstance<
     EnsureNonVoid<M>,
   PublicDefaults = UnwrapMixinsType<PublicMixin, 'Defaults'> &
     EnsureNonVoid<Defaults>,
+  Provide extends ComponentProvideOptions = ComponentProvideOptions,
 > = ComponentPublicInstance<
   PublicP,
   PublicB,
@@ -190,11 +201,22 @@ export type CreateComponentPublicInstance<
     Defaults,
     {},
     string,
-    S
+    S,
+    LC,
+    Directives,
+    Exposed,
+    Provide
   >,
   I,
-  S
+  S,
+  Exposed
 >
+
+export type ExposedKeys<
+  T,
+  Exposed extends string & keyof T,
+> = '' extends Exposed ? T : Pick<T, Exposed>
+
 // public properties exposed on the proxy, which is used as the render context
 // in templates (as `this` in the render option)
 export type ComponentPublicInstance<
@@ -210,6 +232,7 @@ export type ComponentPublicInstance<
   Options = ComponentOptionsBase<any, any, any, any, any, any, any, any, any>,
   I extends ComponentInjectOptions = {},
   S extends SlotsType = {},
+  Exposed extends string = '',
 > = {
   $: ComponentInternalInstance
   $data: D
@@ -233,13 +256,16 @@ export type ComponentPublicInstance<
       : (...args: any) => any,
     options?: WatchOptions,
   ): WatchStopHandle
-} & IfAny<P, P, Omit<P, keyof ShallowUnwrapRef<B>>> &
-  ShallowUnwrapRef<B> &
-  UnwrapNestedRefs<D> &
-  ExtractComputedReturns<C> &
-  M &
-  ComponentCustomProperties &
-  InjectToObject<I>
+} & ExposedKeys<
+  IfAny<P, P, Omit<P, keyof ShallowUnwrapRef<B>>> &
+    ShallowUnwrapRef<B> &
+    UnwrapNestedRefs<D> &
+    ExtractComputedReturns<C> &
+    M &
+    ComponentCustomProperties &
+    InjectToObject<I>,
+  Exposed
+>
 
 export type PublicPropertiesMap = Record<
   string,
index daaf28b15188e4f369038d0762acda2cc9f429b8..c6dce57c1b680a5e189e6edfbf08306518531732 100644 (file)
@@ -26,19 +26,29 @@ import { mapCompatDirectiveHook } from './compat/customDirective'
 import { pauseTracking, resetTracking } from '@vue/reactivity'
 import { traverse } from './apiWatch'
 
-export interface DirectiveBinding<V = any> {
+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<any, V>
+  value: Value
+  oldValue: Value | null
+  arg?: Arg
+  modifiers: DirectiveModifiers<Modifiers>
+  dir: ObjectDirective<any, Value>
 }
 
-export type DirectiveHook<T = any, Prev = VNode<any, T> | null, V = any> = (
-  el: T,
-  binding: DirectiveBinding<V>,
-  vnode: VNode<any, T>,
+export type DirectiveHook<
+  HostElement = any,
+  Prev = VNode<any, HostElement> | null,
+  Value = any,
+  Modifiers extends string = string,
+  Arg extends string = string,
+> = (
+  el: HostElement,
+  binding: DirectiveBinding<Value, Modifiers, Arg>,
+  vnode: VNode<any, HostElement>,
   prevVNode: Prev,
 ) => void
 
@@ -47,25 +57,52 @@ export type SSRDirectiveHook = (
   vnode: VNode,
 ) => Data | undefined
 
-export interface ObjectDirective<T = any, V = any> {
-  created?: DirectiveHook<T, null, V>
-  beforeMount?: DirectiveHook<T, null, V>
-  mounted?: DirectiveHook<T, null, V>
-  beforeUpdate?: DirectiveHook<T, VNode<any, T>, V>
-  updated?: DirectiveHook<T, VNode<any, T>, V>
-  beforeUnmount?: DirectiveHook<T, null, V>
-  unmounted?: DirectiveHook<T, null, V>
+export interface ObjectDirective<
+  HostElement = any,
+  Value = any,
+  Modifiers extends string = string,
+  Arg extends string = string,
+> {
+  created?: DirectiveHook<HostElement, null, Value, Modifiers, Arg>
+  beforeMount?: DirectiveHook<HostElement, null, Value, Modifiers, Arg>
+  mounted?: DirectiveHook<HostElement, null, Value, Modifiers, Arg>
+  beforeUpdate?: DirectiveHook<
+    HostElement,
+    VNode<any, HostElement>,
+    Value,
+    Modifiers,
+    Arg
+  >
+  updated?: DirectiveHook<
+    HostElement,
+    VNode<any, HostElement>,
+    Value,
+    Modifiers,
+    Arg
+  >
+  beforeUnmount?: DirectiveHook<HostElement, null, Value, Modifiers, Arg>
+  unmounted?: DirectiveHook<HostElement, null, Value, Modifiers, Arg>
   getSSRProps?: SSRDirectiveHook
   deep?: boolean
 }
 
-export type FunctionDirective<T = any, V = any> = DirectiveHook<T, any, V>
+export type FunctionDirective<
+  HostElement = any,
+  V = any,
+  Modifiers extends string = string,
+  Arg extends string = string,
+> = DirectiveHook<HostElement, any, V, Modifiers, Arg>
 
-export type Directive<T = any, V = any> =
-  | ObjectDirective<T, V>
-  | FunctionDirective<T, V>
+export type Directive<
+  HostElement = any,
+  Value = any,
+  Modifiers extends string = string,
+  Arg extends string = string,
+> =
+  | ObjectDirective<HostElement, Value, Modifiers, Arg>
+  | FunctionDirective<HostElement, Value, Modifiers, Arg>
 
-export type DirectiveModifiers = Record<string, boolean>
+export type DirectiveModifiers<K extends string = string> = Record<K, boolean>
 
 export function validateDirectiveName(name: string) {
   if (isBuiltInDirective(name)) {
index 94b2985040acfeadf07ce890e0de470b2d5f5d3d..5d36407ba6a625fd2b3a6980b100b746639c74e3 100644 (file)
@@ -248,6 +248,8 @@ export type {
   SetupContext,
   ComponentCustomProps,
   AllowedComponentProps,
+  GlobalComponents,
+  GlobalDirectives,
   ComponentInstance,
 } from './component'
 export type {
diff --git a/packages/runtime-core/types/globalComponents.d.ts b/packages/runtime-core/types/globalComponents.d.ts
new file mode 100644 (file)
index 0000000..a4abd6d
--- /dev/null
@@ -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<TeleportProps>
+    Suspense: DefineComponent<SuspenseProps>
+    KeepAlive: DefineComponent<KeepAliveProps>
+    BaseTransition: DefineComponent<BaseTransitionProps>
+  }
+}
index 03620f747e7abe897990510630db3294433087f4..ef7ee346ba814d963881e4090c39f9ca75fd258d 100644 (file)
@@ -43,7 +43,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')
index b0ea41728bd08443a53d12139e2d0a3b70cdcac0..cb599656f0d7b851cab26ff3cc6ae627e77b6abe 100644 (file)
@@ -39,14 +39,17 @@ function onCompositionEnd(e: Event) {
 
 const assignKey = Symbol('_assign')
 
-type ModelDirective<T> = ObjectDirective<
-  T & { [assignKey]: AssignerFn; _assigning?: boolean }
+type ModelDirective<T, Modifiers extends string = string> = ObjectDirective<
+  T & { [assignKey]: AssignerFn; _assigning?: boolean },
+  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[assignKey] = getModelAssigner(vnode)
@@ -183,7 +186,7 @@ export const vModelRadio: ModelDirective<HTMLInputElement> = {
   },
 }
 
-export const vModelSelect: ModelDirective<HTMLSelectElement> = {
+export const vModelSelect: ModelDirective<HTMLSelectElement, 'number'> = {
   // <select multiple> value need to be deep traversed
   deep: true,
   created(el, { value, modifiers: { number } }, vnode) {
@@ -363,3 +366,10 @@ export function initVModelForSSR() {
     }
   }
 }
+
+export type VModelDirective =
+  | typeof vModelText
+  | typeof vModelCheckbox
+  | typeof vModelSelect
+  | typeof vModelRadio
+  | typeof vModelDynamic
index 5a7d9e4af4a1b2dcad9edd47495599ec82107548..ee96d7b9efb00c682773ee09db779472a82859b7 100644 (file)
@@ -1,33 +1,40 @@
 import {
   type ComponentInternalInstance,
   DeprecationTypes,
+  type Directive,
   type LegacyConfig,
   compatUtils,
   getCurrentInstance,
 } from '@vue/runtime-core'
 import { hyphenate, isArray } from '@vue/shared'
 
-const systemModifiers = ['ctrl', 'shift', 'alt', 'meta']
+const systemModifiers = ['ctrl', 'shift', 'alt', 'meta'] as const
+type SystemModifiers = (typeof systemModifiers)[number]
+type CompatModifiers = keyof typeof keyNames
 
+export type VOnModifiers = SystemModifiers | ModifierGuards | CompatModifiers
 type KeyedEvent = KeyboardEvent | MouseEvent | TouchEvent
 
-const modifierGuards: Record<
-  string,
-  (e: Event, modifiers: string[]) => void | boolean
-> = {
-  stop: e => e.stopPropagation(),
-  prevent: e => e.preventDefault(),
-  self: e => e.target !== e.currentTarget,
-  ctrl: e => !(e as KeyedEvent).ctrlKey,
-  shift: e => !(e as KeyedEvent).shiftKey,
-  alt: e => !(e as KeyedEvent).altKey,
-  meta: e => !(e as KeyedEvent).metaKey,
-  left: e => 'button' in e && (e as MouseEvent).button !== 0,
-  middle: e => 'button' in e && (e as MouseEvent).button !== 1,
-  right: e => 'button' in e && (e as MouseEvent).button !== 2,
+const modifierGuards = {
+  stop: (e: Event) => e.stopPropagation(),
+  prevent: (e: Event) => e.preventDefault(),
+  self: (e: Event) => e.target !== e.currentTarget,
+  ctrl: (e: Event) => !(e as KeyedEvent).ctrlKey,
+  shift: (e: Event) => !(e as KeyedEvent).shiftKey,
+  alt: (e: Event) => !(e as KeyedEvent).altKey,
+  meta: (e: Event) => !(e as KeyedEvent).metaKey,
+  left: (e: Event) => 'button' in e && (e as MouseEvent).button !== 0,
+  middle: (e: Event) => 'button' in e && (e as MouseEvent).button !== 1,
+  right: (e: Event) => 'button' in e && (e as MouseEvent).button !== 2,
   exact: (e, modifiers) =>
     systemModifiers.some(m => (e as any)[`${m}Key`] && !modifiers.includes(m)),
-}
+} satisfies Record<
+  string,
+  | ((e: Event) => void | boolean)
+  | ((e: Event, modifiers: string[]) => void | boolean)
+>
+
+type ModifierGuards = keyof typeof modifierGuards
 
 /**
  * @private
@@ -36,7 +43,7 @@ export const withModifiers = <
   T extends (event: Event, ...args: unknown[]) => any,
 >(
   fn: T & { _withMods?: { [key: string]: T } },
-  modifiers: string[],
+  modifiers: VOnModifiers[],
 ) => {
   const cache = fn._withMods || (fn._withMods = {})
   const cacheKey = modifiers.join('.')
@@ -44,7 +51,7 @@ export const withModifiers = <
     cache[cacheKey] ||
     (cache[cacheKey] = ((event, ...args) => {
       for (let i = 0; i < modifiers.length; i++) {
-        const guard = modifierGuards[modifiers[i]]
+        const guard = modifierGuards[modifiers[i] as ModifierGuards]
         if (guard && guard(event, modifiers)) return
       }
       return fn(event, ...args)
@@ -54,7 +61,7 @@ export const withModifiers = <
 
 // Kept for 2.x compat.
 // Note: IE11 compat for `spacebar` and `del` is removed for now.
-const keyNames: Record<string, string | string[]> = {
+const keyNames = {
   esc: 'escape',
   space: ' ',
   up: 'arrow-up',
@@ -62,7 +69,7 @@ const keyNames: Record<string, string | string[]> = {
   right: 'arrow-right',
   down: 'arrow-down',
   delete: 'backspace',
-}
+} satisfies Record<string, string | string[]>
 
 /**
  * @private
@@ -101,7 +108,13 @@ export const withKeys = <T extends (event: KeyboardEvent) => any>(
       }
 
       const eventKey = hyphenate(event.key)
-      if (modifiers.some(k => k === eventKey || keyNames[k] === eventKey)) {
+      if (
+        modifiers.some(
+          k =>
+            k === eventKey ||
+            keyNames[k as unknown as CompatModifiers] === eventKey,
+        )
+      ) {
         return fn(event)
       }
 
@@ -133,3 +146,5 @@ export const withKeys = <T extends (event: KeyboardEvent) => any>(
     }) as T)
   )
 }
+
+export type VOnDirective = Directive<any, any, VOnModifiers>
index 44ada3d935f4ae7bbf0bf007e884fb833fb6697c..ab85720faa8fa900a56e740f82bc11fdd8684ce0 100644 (file)
@@ -1,7 +1,9 @@
 import {
   type App,
   type CreateAppFunction,
+  type DefineComponent,
   DeprecationTypes,
+  type Directive,
   type ElementNamespace,
   type HydrationRenderer,
   type Renderer,
@@ -25,6 +27,11 @@ import {
   isSVGTag,
   isString,
 } from '@vue/shared'
+import type { TransitionProps } from './components/Transition'
+import type { TransitionGroupProps } from './components/TransitionGroup'
+import type { vShow } from './directives/vShow'
+import type { VOnDirective } from './directives/vOn'
+import type { VModelDirective } from './directives/vModel'
 
 declare module '@vue/reactivity' {
   export interface RefUnwrapBailTypes {
@@ -32,6 +39,22 @@ declare module '@vue/reactivity' {
   }
 }
 
+declare module '@vue/runtime-core' {
+  interface GlobalComponents {
+    Transition: DefineComponent<TransitionProps>
+    TransitionGroup: DefineComponent<TransitionGroupProps>
+  }
+
+  interface GlobalDirectives {
+    vShow: typeof vShow
+    vOn: VOnDirective
+    vBind: VModelDirective
+    vIf: Directive<any, boolean>
+    VOnce: Directive
+    VSlot: Directive
+  }
+}
+
 const rendererOptions = /*#__PURE__*/ extend({ patchProp }, nodeOps)
 
 // lazy create the renderer - this makes core renderer logic tree-shakable
index 1feccabd8f820bc3c7f67ec9a31a038adf7102a3..fd0432796b2073442716c13f5f2cb3a3d7a18fde 100644 (file)
@@ -292,7 +292,7 @@ describe('INSTANCE_SCOPED_SLOTS', () => {
       components: {
         child: {
           compatConfig: { RENDER_FUNCTION: false },
-          render() {
+          render(this: LegacyPublicInstance) {
             normalSlots = this.$slots
             scopedSlots = this.$scopedSlots
           },