]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat(types): provide internal options for directly using user types in language tools...
authorEvan You <yyx990803@gmail.com>
Sat, 27 Apr 2024 03:48:37 +0000 (11:48 +0800)
committerGitHub <noreply@github.com>
Sat, 27 Apr 2024 03:48:37 +0000 (11:48 +0800)
packages/dts-test/defineComponent.test-d.tsx
packages/runtime-core/src/apiDefineComponent.ts
packages/runtime-core/src/apiSetupHelpers.ts
packages/runtime-core/src/componentEmits.ts
packages/runtime-core/src/componentOptions.ts
packages/runtime-core/src/componentProps.ts
packages/runtime-core/src/index.ts
packages/runtime-dom/__tests__/customElement.spec.ts
packages/runtime-dom/src/apiCustomElement.ts
packages/shared/src/typeUtils.ts

index 44a00d4e586efe9c885f78b3db879607a7f33020..077f1abc0751c22bb19875a5602a66c2333220ed 100644 (file)
@@ -15,7 +15,7 @@ import {
   withKeys,
   withModifiers,
 } from 'vue'
-import { type IsUnion, describe, expectType } from './utils'
+import { type IsAny, type IsUnion, describe, expectType } from './utils'
 
 describe('with object props', () => {
   interface ExpectedProps {
@@ -1623,3 +1623,146 @@ declare const MyButton: DefineComponent<
   {}
 >
 ;<MyButton class="x" />
+
+describe('__typeProps backdoor for union type for conditional props', () => {
+  interface CommonProps {
+    size?: 'xl' | 'l' | 'm' | 's' | 'xs'
+  }
+
+  type ConditionalProps =
+    | {
+        color?: 'normal' | 'primary' | 'secondary'
+        appearance?: 'normal' | 'outline' | 'text'
+      }
+    | {
+        color: 'white'
+        appearance: 'outline'
+      }
+
+  type Props = CommonProps & ConditionalProps
+
+  const Comp = defineComponent({
+    __typeProps: {} as Props,
+  })
+  // @ts-expect-error
+  ;<Comp color="white" />
+  // @ts-expect-error
+  ;<Comp color="white" appearance="normal" />
+  ;<Comp color="white" appearance="outline" />
+
+  const c = new Comp()
+
+  // @ts-expect-error
+  c.$props = { color: 'white' }
+  // @ts-expect-error
+  c.$props = { color: 'white', appearance: 'text' }
+  c.$props = { color: 'white', appearance: 'outline' }
+})
+
+describe('__typeEmits backdoor, 3.3+ object syntax', () => {
+  type Emits = {
+    change: [id: number]
+    update: [value: string]
+  }
+
+  const Comp = defineComponent({
+    __typeEmits: {} as Emits,
+    mounted() {
+      this.$props.onChange?.(123)
+      // @ts-expect-error
+      this.$props.onChange?.('123')
+      this.$props.onUpdate?.('foo')
+      // @ts-expect-error
+      this.$props.onUpdate?.(123)
+
+      // @ts-expect-error
+      this.$emit('foo')
+
+      this.$emit('change', 123)
+      // @ts-expect-error
+      this.$emit('change', '123')
+
+      this.$emit('update', 'test')
+      // @ts-expect-error
+      this.$emit('update', 123)
+    },
+  })
+
+  ;<Comp onChange={id => id.toFixed(2)} />
+  ;<Comp onUpdate={id => id.toUpperCase()} />
+  // @ts-expect-error
+  ;<Comp onChange={id => id.slice(1)} />
+  // @ts-expect-error
+  ;<Comp onUpdate={id => id.toFixed(2)} />
+
+  const c = new Comp()
+  // @ts-expect-error
+  c.$emit('foo')
+
+  c.$emit('change', 123)
+  // @ts-expect-error
+  c.$emit('change', '123')
+
+  c.$emit('update', 'test')
+  // @ts-expect-error
+  c.$emit('update', 123)
+})
+
+describe('__typeEmits backdoor, call signature syntax', () => {
+  type Emits = {
+    (e: 'change', id: number): void
+    (e: 'update', value: string): void
+  }
+
+  const Comp = defineComponent({
+    __typeEmits: {} as Emits,
+    mounted() {
+      this.$props.onChange?.(123)
+      // @ts-expect-error
+      this.$props.onChange?.('123')
+      this.$props.onUpdate?.('foo')
+      // @ts-expect-error
+      this.$props.onUpdate?.(123)
+
+      // @ts-expect-error
+      this.$emit('foo')
+
+      this.$emit('change', 123)
+      // @ts-expect-error
+      this.$emit('change', '123')
+
+      this.$emit('update', 'test')
+      // @ts-expect-error
+      this.$emit('update', 123)
+    },
+  })
+
+  ;<Comp onChange={id => id.toFixed(2)} />
+  ;<Comp onUpdate={id => id.toUpperCase()} />
+  // @ts-expect-error
+  ;<Comp onChange={id => id.slice(1)} />
+  // @ts-expect-error
+  ;<Comp onUpdate={id => id.toFixed(2)} />
+
+  const c = new Comp()
+  // @ts-expect-error
+  c.$emit('foo')
+
+  c.$emit('change', 123)
+  // @ts-expect-error
+  c.$emit('change', '123')
+
+  c.$emit('update', 'test')
+  // @ts-expect-error
+  c.$emit('update', 123)
+})
+
+defineComponent({
+  props: {
+    foo: [String, null],
+  },
+  setup(props) {
+    expectType<IsAny<typeof props.foo>>(false)
+    expectType<string | null | undefined>(props.foo)
+  },
+})
index 46e1d59a8e7328d6de9561cc04f7aafd6304b8f2..7fce96586da1888209f9ec1e7046b3f552792166 100644 (file)
@@ -3,9 +3,6 @@ import type {
   ComponentOptions,
   ComponentOptionsBase,
   ComponentOptionsMixin,
-  ComponentOptionsWithArrayProps,
-  ComponentOptionsWithObjectProps,
-  ComponentOptionsWithoutProps,
   ComponentProvideOptions,
   ComputedOptions,
   MethodOptions,
@@ -25,7 +22,11 @@ import type {
   ExtractDefaultPropTypes,
   ExtractPropTypes,
 } from './componentProps'
-import type { EmitsOptions, EmitsToProps } from './componentEmits'
+import type {
+  EmitsOptions,
+  EmitsToProps,
+  TypeEmitsToOptions,
+} from './componentEmits'
 import { extend, isFunction } from '@vue/shared'
 import type { VNodeProps } from './vnode'
 import type {
@@ -34,6 +35,7 @@ import type {
 } from './componentPublicInstance'
 import type { SlotsType } from './componentSlots'
 import type { Directive } from './directives'
+import type { ComponentTypeEmits } from './apiSetupHelpers'
 
 export type PublicProps = VNodeProps &
   AllowedComponentProps &
@@ -64,6 +66,7 @@ export type DefineComponent<
   Directives extends Record<string, Directive> = {},
   Exposed extends string = string,
   Provide extends ComponentProvideOptions = ComponentProvideOptions,
+  MakeDefaultsOptional extends boolean = true,
 > = ComponentPublicInstanceConstructor<
   CreateComponentPublicInstance<
     Props,
@@ -76,7 +79,7 @@ export type DefineComponent<
     E,
     PP & Props,
     Defaults,
-    true,
+    MakeDefaultsOptional,
     {},
     S,
     LC & GlobalComponents,
@@ -169,183 +172,114 @@ export function defineComponent<
   },
 ): DefineSetupFnComponent<Props, E, S>
 
-// overload 2: object format with no props
-// (uses user defined props interface)
-// return type is for Vetur and TSX support
-export function defineComponent<
-  Props = {},
-  RawBindings = {},
-  D = {},
-  C extends ComputedOptions = {},
-  M extends MethodOptions = {},
-  Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
-  Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
-  E extends EmitsOptions = {},
-  EE extends string = string,
-  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,
-    RawBindings,
-    D,
-    C,
-    M,
-    Mixin,
-    Extends,
-    E,
-    EE,
-    I,
-    II,
-    S,
-    LC,
-    Directives,
-    Exposed,
-    Provide
-  >,
-): DefineComponent<
-  Props,
-  RawBindings,
-  D,
-  C,
-  M,
-  Mixin,
-  Extends,
-  E,
-  EE,
-  PublicProps,
-  ResolveProps<Props, E>,
-  ExtractDefaultPropTypes<Props>,
-  S,
-  LC,
-  Directives,
-  Exposed,
-  Provide
->
-
-// 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,
-  C extends ComputedOptions = {},
-  M extends MethodOptions = {},
-  Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
-  Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
-  E extends EmitsOptions = {},
-  EE extends string = string,
-  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<
-    PropNames,
-    RawBindings,
-    D,
-    C,
-    M,
-    Mixin,
-    Extends,
-    E,
-    EE,
-    I,
-    II,
-    S,
-    LC,
-    Directives,
-    Exposed,
-    Provide
-  >,
-): DefineComponent<
-  Props,
-  RawBindings,
-  D,
-  C,
-  M,
-  Mixin,
-  Extends,
-  E,
-  EE,
-  PublicProps,
-  ResolveProps<Props, E>,
-  ExtractDefaultPropTypes<Props>,
-  S,
-  LC,
-  Directives,
-  Exposed,
-  Provide
->
-
-// overload 4: object format with object props declaration
-// see `ExtractPropTypes` in ./componentProps.ts
+// overload 2: defineComponent with options object, infer props from options
 export function defineComponent<
-  // the Readonly constraint allows TS to treat the type of { required: true }
-  // as constant instead of boolean.
-  PropsOptions extends Readonly<ComponentPropsOptions>,
-  RawBindings,
-  D,
-  C extends ComputedOptions = {},
-  M extends MethodOptions = {},
+  // props
+  TypeProps,
+  RuntimePropsOptions extends
+    ComponentObjectPropsOptions = ComponentObjectPropsOptions,
+  RuntimePropsKeys extends string = string,
+  // emits
+  TypeEmits extends ComponentTypeEmits = {},
+  RuntimeEmitsOptions extends EmitsOptions = {},
+  RuntimeEmitsKeys extends string = string,
+  // other options
+  Data = {},
+  SetupBindings = {},
+  Computed extends ComputedOptions = {},
+  Methods extends MethodOptions = {},
   Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
   Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
-  E extends EmitsOptions = {},
-  EE extends string = string,
-  I extends ComponentInjectOptions = {},
-  II extends string = string,
-  S extends SlotsType = {},
-  LC extends Record<string, Component> = {},
+  InjectOptions extends ComponentInjectOptions = {},
+  InjectKeys extends string = string,
+  Slots extends SlotsType = {},
+  LocalComponents extends Record<string, Component> = {},
   Directives extends Record<string, Directive> = {},
   Exposed extends string = string,
   Provide extends ComponentProvideOptions = ComponentProvideOptions,
+  // resolved types
+  ResolvedEmits extends EmitsOptions = {} extends RuntimeEmitsOptions
+    ? TypeEmitsToOptions<TypeEmits>
+    : RuntimeEmitsOptions,
+  InferredProps = unknown extends TypeProps
+    ? string extends RuntimePropsKeys
+      ? ComponentObjectPropsOptions extends RuntimePropsOptions
+        ? {}
+        : ExtractPropTypes<RuntimePropsOptions>
+      : { [key in RuntimePropsKeys]?: any }
+    : TypeProps,
+  ResolvedProps = Readonly<InferredProps & EmitsToProps<ResolvedEmits>>,
 >(
-  options: ComponentOptionsWithObjectProps<
-    PropsOptions,
-    RawBindings,
-    D,
-    C,
-    M,
+  options: {
+    props?: (RuntimePropsOptions & ThisType<void>) | RuntimePropsKeys[]
+    /**
+     * @private for language-tools use only
+     */
+    __typeProps?: TypeProps
+    /**
+     * @private for language-tools use only
+     */
+    __typeEmits?: TypeEmits
+  } & ComponentOptionsBase<
+    ResolvedProps,
+    SetupBindings,
+    Data,
+    Computed,
+    Methods,
     Mixin,
     Extends,
-    E,
-    EE,
-    I,
-    II,
-    S,
-    LC,
+    RuntimeEmitsOptions,
+    RuntimeEmitsKeys,
+    {}, // Defaults
+    InjectOptions,
+    InjectKeys,
+    Slots,
+    LocalComponents,
     Directives,
     Exposed,
     Provide
-  >,
+  > &
+    ThisType<
+      CreateComponentPublicInstance<
+        ResolvedProps,
+        SetupBindings,
+        Data,
+        Computed,
+        Methods,
+        Mixin,
+        Extends,
+        ResolvedEmits,
+        RuntimeEmitsKeys,
+        {},
+        false,
+        InjectOptions,
+        Slots,
+        LocalComponents,
+        Directives,
+        Exposed
+      >
+    >,
 ): DefineComponent<
-  PropsOptions,
-  RawBindings,
-  D,
-  C,
-  M,
+  InferredProps,
+  SetupBindings,
+  Data,
+  Computed,
+  Methods,
   Mixin,
   Extends,
-  E,
-  EE,
+  ResolvedEmits,
+  RuntimeEmitsKeys,
   PublicProps,
-  ResolveProps<PropsOptions, E>,
-  ExtractDefaultPropTypes<PropsOptions>,
-  S,
-  LC,
+  ResolvedProps,
+  ExtractDefaultPropTypes<RuntimePropsOptions>,
+  Slots,
+  LocalComponents,
   Directives,
   Exposed,
-  Provide
+  Provide,
+  // MakeDefaultsOptional - if TypeProps is provided, set to false to use
+  // user props types verbatim
+  unknown extends TypeProps ? true : false
 >
 
 // implementation, close to no-op
index 382bb30b3650aad287905c0001717e32f747a725..dbe27dde48ee7da499211a29b8c99767fd714bd6 100644 (file)
@@ -16,8 +16,8 @@ import {
 } from './component'
 import type { EmitFn, EmitsOptions, ObjectEmitsOptions } from './componentEmits'
 import type {
+  ComponentOptionsBase,
   ComponentOptionsMixin,
-  ComponentOptionsWithoutProps,
   ComputedOptions,
   MethodOptions,
 } from './componentOptions'
@@ -135,9 +135,11 @@ export function defineEmits<EE extends string = string>(
 export function defineEmits<E extends EmitsOptions = EmitsOptions>(
   emitOptions: E,
 ): EmitFn<E>
-export function defineEmits<
-  T extends ((...args: any[]) => any) | Record<string, any[]>,
->(): T extends (...args: any[]) => any ? T : ShortEmits<T>
+export function defineEmits<T extends ComponentTypeEmits>(): T extends (
+  ...args: any[]
+) => any
+  ? T
+  : ShortEmits<T>
 // implementation
 export function defineEmits() {
   if (__DEV__) {
@@ -146,6 +148,10 @@ export function defineEmits() {
   return null as any
 }
 
+export type ComponentTypeEmits =
+  | ((...args: any[]) => any)
+  | Record<string, any[]>
+
 type RecordToUnion<T extends Record<string, any>> = T[keyof T]
 
 type ShortEmits<T extends Record<string, any>> = UnionToIntersection<
@@ -191,15 +197,33 @@ export function defineOptions<
   Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
   Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
 >(
-  options?: ComponentOptionsWithoutProps<
+  options?: ComponentOptionsBase<
     {},
     RawBindings,
     D,
     C,
     M,
     Mixin,
-    Extends
-  > & { emits?: undefined; expose?: undefined; slots?: undefined },
+    Extends,
+    {}
+  > & {
+    /**
+     * props should be defined via defineProps().
+     */
+    props: never
+    /**
+     * emits should be defined via defineEmits().
+     */
+    emits?: never
+    /**
+     * expose should be defined via defineExpose().
+     */
+    expose?: never
+    /**
+     * slots should be defined via defineSlots().
+     */
+    slots?: never
+  },
 ): void {
   if (__DEV__) {
     warnRuntimeUsage(`defineOptions`)
index 4551235bc5a00ada196bd5f0972e952deeda4b4d..eb80d02afb6b3b7f0c371cf19e41832c1379a221 100644 (file)
@@ -1,5 +1,6 @@
 import {
   EMPTY_OBJ,
+  type OverloadParameters,
   type UnionToIntersection,
   camelize,
   extend,
@@ -28,6 +29,7 @@ import {
   compatModelEmit,
   compatModelEventPrefix,
 } from './compat/componentVModel'
+import type { ComponentTypeEmits } from './apiSetupHelpers'
 
 export type ObjectEmitsOptions = Record<
   string,
@@ -36,23 +38,41 @@ export type ObjectEmitsOptions = Record<
 
 export type EmitsOptions = ObjectEmitsOptions | string[]
 
-export type EmitsToProps<T extends EmitsOptions> = T extends string[]
-  ? {
-      [K in `on${Capitalize<T[number]>}`]?: (...args: any[]) => any
-    }
-  : T extends ObjectEmitsOptions
+export type EmitsToProps<T extends EmitsOptions | ComponentTypeEmits> =
+  T extends string[]
     ? {
-        [K in `on${Capitalize<string & keyof T>}`]?: K extends `on${infer C}`
-          ? (
-              ...args: T[Uncapitalize<C>] extends (...args: infer P) => any
-                ? P
-                : T[Uncapitalize<C>] extends null
-                  ? any[]
-                  : never
-            ) => any
-          : never
+        [K in `on${Capitalize<T[number]>}`]?: (...args: any[]) => any
       }
-    : {}
+    : T extends ObjectEmitsOptions
+      ? {
+          [K in `on${Capitalize<string & keyof T>}`]?: K extends `on${infer C}`
+            ? (
+                ...args: T[Uncapitalize<C>] extends (...args: infer P) => any
+                  ? P
+                  : T[Uncapitalize<C>] extends null
+                    ? any[]
+                    : never
+              ) => any
+            : never
+        }
+      : {}
+
+export type TypeEmitsToOptions<T extends ComponentTypeEmits> =
+  T extends Record<string, any[]>
+    ? {
+        [K in keyof T]: T[K] extends [...args: infer Args]
+          ? (...args: Args) => any
+          : () => any
+      }
+    : T extends (...args: any[]) => any
+      ? ParametersToFns<OverloadParameters<T>>
+      : {}
+
+type ParametersToFns<T extends any[]> = {
+  [K in T[0]]: K extends `${infer C}`
+    ? (...args: T extends [C, ...infer Args] ? Args : never) => any
+    : never
+}
 
 export type ShortEmitsToObject<E> =
   E extends Record<string, any[]>
index da1dfcec0cc6f3ccd28a609a9a28b5937f0f2a7e..ac1841edee9aa501b096eabe707f10ac7abee260 100644 (file)
@@ -54,7 +54,11 @@ import type {
   ExtractDefaultPropTypes,
   ExtractPropTypes,
 } from './componentProps'
-import type { EmitsOptions, EmitsToProps } from './componentEmits'
+import type {
+  EmitsOptions,
+  EmitsToProps,
+  TypeEmitsToOptions,
+} from './componentEmits'
 import type { Directive } from './directives'
 import {
   type ComponentPublicInstance,
@@ -76,7 +80,10 @@ import {
 import type { OptionMergeFunction } from './apiCreateApp'
 import { LifecycleHooks } from './enums'
 import type { SlotsType } from './componentSlots'
-import { normalizePropsOrEmits } from './apiSetupHelpers'
+import {
+  type ComponentTypeEmits,
+  normalizePropsOrEmits,
+} from './apiSetupHelpers'
 
 /**
  * Interface for declaring custom options.
@@ -218,183 +225,6 @@ export interface RuntimeCompilerOptions {
   delimiters?: [string, string]
 }
 
-export type ComponentOptionsWithoutProps<
-  Props = {},
-  RawBindings = {},
-  D = {},
-  C extends ComputedOptions = {},
-  M extends MethodOptions = {},
-  Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
-  Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
-  E extends EmitsOptions = EmitsOptions,
-  EE extends string = string,
-  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,
-  RawBindings,
-  D,
-  C,
-  M,
-  Mixin,
-  Extends,
-  E,
-  EE,
-  {},
-  I,
-  II,
-  S,
-  LC,
-  Directives,
-  Exposed,
-  Provide
-> & {
-  props?: undefined
-} & ThisType<
-    CreateComponentPublicInstance<
-      PE,
-      RawBindings,
-      D,
-      C,
-      M,
-      Mixin,
-      Extends,
-      E,
-      PE,
-      {},
-      false,
-      I,
-      S,
-      LC,
-      Directives,
-      Exposed
-    >
-  >
-
-export type ComponentOptionsWithArrayProps<
-  PropNames extends string = string,
-  RawBindings = {},
-  D = {},
-  C extends ComputedOptions = {},
-  M extends MethodOptions = {},
-  Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
-  Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
-  E extends EmitsOptions = EmitsOptions,
-  EE extends string = string,
-  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,
-  RawBindings,
-  D,
-  C,
-  M,
-  Mixin,
-  Extends,
-  E,
-  EE,
-  {},
-  I,
-  II,
-  S,
-  LC,
-  Directives,
-  Exposed,
-  Provide
-> & {
-  props: PropNames[]
-} & ThisType<
-    CreateComponentPublicInstance<
-      Props,
-      RawBindings,
-      D,
-      C,
-      M,
-      Mixin,
-      Extends,
-      E,
-      Props,
-      {},
-      false,
-      I,
-      S,
-      LC,
-      Directives,
-      Exposed
-    >
-  >
-
-export type ComponentOptionsWithObjectProps<
-  PropsOptions = ComponentObjectPropsOptions,
-  RawBindings = {},
-  D = {},
-  C extends ComputedOptions = {},
-  M extends MethodOptions = {},
-  Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
-  Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
-  E extends EmitsOptions = EmitsOptions,
-  EE extends string = string,
-  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<
-  Props,
-  RawBindings,
-  D,
-  C,
-  M,
-  Mixin,
-  Extends,
-  E,
-  EE,
-  Defaults,
-  I,
-  II,
-  S,
-  LC,
-  Directives,
-  Exposed,
-  Provide
-> & {
-  props: PropsOptions & ThisType<void>
-} & ThisType<
-    CreateComponentPublicInstance<
-      Props,
-      RawBindings,
-      D,
-      C,
-      M,
-      Mixin,
-      Extends,
-      E,
-      Props,
-      Defaults,
-      false,
-      I,
-      S,
-      LC,
-      Directives
-    >
-  >
-
 export type ComponentOptions<
   Props = {},
   RawBindings = any,
@@ -1238,3 +1068,203 @@ function mergeWatchOptions(
   }
   return merged
 }
+
+// Deprecated legacy types, kept because they were previously exported ---------
+
+/**
+ * @deprecated
+ */
+export type ComponentOptionsWithoutProps<
+  Props = {},
+  RawBindings = {},
+  D = {},
+  C extends ComputedOptions = {},
+  M extends MethodOptions = {},
+  Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
+  Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
+  E extends EmitsOptions = {},
+  EE extends string = string,
+  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,
+  TE extends ComponentTypeEmits = {},
+  ResolvedEmits extends EmitsOptions = {} extends E
+    ? TypeEmitsToOptions<TE>
+    : E,
+  PE = Props & EmitsToProps<ResolvedEmits>,
+> = ComponentOptionsBase<
+  PE,
+  RawBindings,
+  D,
+  C,
+  M,
+  Mixin,
+  Extends,
+  E,
+  EE,
+  {},
+  I,
+  II,
+  S,
+  LC,
+  Directives,
+  Exposed,
+  Provide
+> & {
+  props?: never
+  /**
+   * @private for language-tools use only
+   */
+  __typeProps?: Props
+  /**
+   * @private for language-tools use only
+   */
+  __typeEmits?: TE
+} & ThisType<
+    CreateComponentPublicInstance<
+      PE,
+      RawBindings,
+      D,
+      C,
+      M,
+      Mixin,
+      Extends,
+      ResolvedEmits,
+      EE,
+      {},
+      false,
+      I,
+      S,
+      LC,
+      Directives,
+      Exposed
+    >
+  >
+
+/**
+ * @deprecated
+ */
+export type ComponentOptionsWithArrayProps<
+  PropNames extends string = string,
+  RawBindings = {},
+  D = {},
+  C extends ComputedOptions = {},
+  M extends MethodOptions = {},
+  Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
+  Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
+  E extends EmitsOptions = EmitsOptions,
+  EE extends string = string,
+  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,
+  RawBindings,
+  D,
+  C,
+  M,
+  Mixin,
+  Extends,
+  E,
+  EE,
+  {},
+  I,
+  II,
+  S,
+  LC,
+  Directives,
+  Exposed,
+  Provide
+> & {
+  props: PropNames[]
+} & ThisType<
+    CreateComponentPublicInstance<
+      Props,
+      RawBindings,
+      D,
+      C,
+      M,
+      Mixin,
+      Extends,
+      E,
+      Props,
+      {},
+      false,
+      I,
+      S,
+      LC,
+      Directives,
+      Exposed
+    >
+  >
+
+/**
+ * @deprecated
+ */
+export type ComponentOptionsWithObjectProps<
+  PropsOptions = ComponentObjectPropsOptions,
+  RawBindings = {},
+  D = {},
+  C extends ComputedOptions = {},
+  M extends MethodOptions = {},
+  Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
+  Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
+  E extends EmitsOptions = EmitsOptions,
+  EE extends string = string,
+  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<
+  Props,
+  RawBindings,
+  D,
+  C,
+  M,
+  Mixin,
+  Extends,
+  E,
+  EE,
+  Defaults,
+  I,
+  II,
+  S,
+  LC,
+  Directives,
+  Exposed,
+  Provide
+> & {
+  props: PropsOptions & ThisType<void>
+} & ThisType<
+    CreateComponentPublicInstance<
+      Props,
+      RawBindings,
+      D,
+      C,
+      M,
+      Mixin,
+      Extends,
+      E,
+      Props,
+      Defaults,
+      false,
+      I,
+      S,
+      LC,
+      Directives
+    >
+  >
index 5a4292b6f36d8630f0b23bf23c2399ac8ad52677..597a1f1f285e928838ba057c732c1e1ef25aedac 100644 (file)
@@ -67,7 +67,7 @@ export interface PropOptions<T = any, D = T> {
   skipFactory?: boolean
 }
 
-export type PropType<T> = PropConstructor<T> | PropConstructor<T>[]
+export type PropType<T> = PropConstructor<T> | (PropConstructor<T> | null)[]
 
 type PropConstructor<T = any> =
   | { new (...args: any[]): T & {} }
@@ -107,8 +107,10 @@ type DefaultKeys<T> = {
     : never
 }[keyof T]
 
-type InferPropType<T> = [T] extends [null]
-  ? any // null & true would fail to infer
+type InferPropType<T, NullAsAny = true> = [T] extends [null]
+  ? NullAsAny extends true
+    ? any
+    : null
   : [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`
     : [T] extends [ObjectConstructor | { type: ObjectConstructor }]
@@ -119,8 +121,8 @@ type InferPropType<T> = [T] extends [null]
           ? Date
           : [T] extends [(infer U)[] | { type: (infer U)[] }]
             ? U extends DateConstructor
-              ? Date | InferPropType<U>
-              : InferPropType<U>
+              ? Date | InferPropType<U, false>
+              : InferPropType<U, false>
             : [T] extends [Prop<infer V, infer D>]
               ? unknown extends V
                 ? IfAny<V, V, D>
@@ -594,7 +596,7 @@ function validatePropName(key: string) {
 
 // use function string name to check type constructors
 // so that it works across vms / iframes.
-function getType(ctor: Prop<any>): string {
+function getType(ctor: Prop<any> | null): string {
   // Early return for null to avoid unnecessary computations
   if (ctor === null) {
     return 'null'
@@ -614,7 +616,7 @@ function getType(ctor: Prop<any>): string {
   return ''
 }
 
-function isSameType(a: Prop<any>, b: Prop<any>): boolean {
+function isSameType(a: Prop<any> | null, b: Prop<any> | null): boolean {
   return getType(a) === getType(b)
 }
 
@@ -707,24 +709,27 @@ type AssertionResult = {
 /**
  * dev only
  */
-function assertType(value: unknown, type: PropConstructor): AssertionResult {
+function assertType(
+  value: unknown,
+  type: PropConstructor | null,
+): AssertionResult {
   let valid
   const expectedType = getType(type)
-  if (isSimpleType(expectedType)) {
+  if (expectedType === 'null') {
+    valid = value === null
+  } else if (isSimpleType(expectedType)) {
     const t = typeof value
     valid = t === expectedType.toLowerCase()
     // for primitive wrapper objects
     if (!valid && t === 'object') {
-      valid = value instanceof type
+      valid = value instanceof (type as PropConstructor)
     }
   } else if (expectedType === 'Object') {
     valid = isObject(value)
   } else if (expectedType === 'Array') {
     valid = isArray(value)
-  } else if (expectedType === 'null') {
-    valid = value === null
   } else {
-    valid = value instanceof type
+    valid = value instanceof (type as PropConstructor)
   }
   return {
     valid,
index 5d36407ba6a625fd2b3a6980b100b746639c74e3..e4a9e53f29c2dd5a54989068b32f908dbeec067c 100644 (file)
@@ -76,6 +76,7 @@ export {
   withDefaults,
   type DefineProps,
   type ModelRef,
+  type ComponentTypeEmits,
 } from './apiSetupHelpers'
 
 /**
@@ -260,9 +261,6 @@ export type {
 export type {
   ComponentOptions,
   ComponentOptionsMixin,
-  ComponentOptionsWithoutProps,
-  ComponentOptionsWithObjectProps,
-  ComponentOptionsWithArrayProps,
   ComponentCustomOptions,
   ComponentOptionsBase,
   ComponentProvideOptions,
@@ -272,7 +270,11 @@ export type {
   RuntimeCompilerOptions,
   ComponentInjectOptions,
 } from './componentOptions'
-export type { EmitsOptions, ObjectEmitsOptions } from './componentEmits'
+export type {
+  EmitsOptions,
+  ObjectEmitsOptions,
+  EmitsToProps,
+} from './componentEmits'
 export type {
   ComponentPublicInstance,
   ComponentCustomProperties,
index fb746f72c4a2ac85ebd602b1ef4e1f6d24c0acc4..cc56de2d6850a87f267c5c051143de971323fd4c 100644 (file)
@@ -88,10 +88,14 @@ describe('defineCustomElement', () => {
 
   describe('props', () => {
     const E = defineCustomElement({
-      props: ['foo', 'bar', 'bazQux'],
+      props: {
+        foo: [String, null],
+        bar: Object,
+        bazQux: null,
+      },
       render() {
         return [
-          h('div', null, this.foo),
+          h('div', null, this.foo || ''),
           h('div', null, this.bazQux || (this.bar && this.bar.x)),
         ]
       },
index 01ce2bad4644681273a2ba6b51ec4008f05c40b5..017284662419541551ae63b568c6acaa5c150e9b 100644 (file)
@@ -1,16 +1,19 @@
 import {
+  type Component,
   type ComponentInjectOptions,
   type ComponentInternalInstance,
+  type ComponentObjectPropsOptions,
   type ComponentOptions,
+  type ComponentOptionsBase,
   type ComponentOptionsMixin,
-  type ComponentOptionsWithArrayProps,
-  type ComponentOptionsWithObjectProps,
-  type ComponentOptionsWithoutProps,
-  type ComponentPropsOptions,
+  type ComponentProvideOptions,
   type ComputedOptions,
   type ConcreteComponent,
+  type CreateComponentPublicInstance,
   type DefineComponent,
+  type Directive,
   type EmitsOptions,
+  type EmitsToProps,
   type ExtractPropTypes,
   type MethodOptions,
   type RenderFunction,
@@ -41,98 +44,79 @@ export function defineCustomElement<Props, RawBindings = object>(
   ) => RawBindings | RenderFunction,
 ): VueElementConstructor<Props>
 
-// overload 2: object format with no props
+// overload 2: defineCustomElement with options object, infer props from options
 export function defineCustomElement<
-  Props = {},
-  RawBindings = {},
-  D = {},
-  C extends ComputedOptions = {},
-  M extends MethodOptions = {},
+  // props
+  RuntimePropsOptions extends
+    ComponentObjectPropsOptions = ComponentObjectPropsOptions,
+  PropsKeys extends string = string,
+  // emits
+  RuntimeEmitsOptions extends EmitsOptions = {},
+  EmitsKeys extends string = string,
+  // other options
+  Data = {},
+  SetupBindings = {},
+  Computed extends ComputedOptions = {},
+  Methods extends MethodOptions = {},
   Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
   Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
-  E extends EmitsOptions = EmitsOptions,
-  EE extends string = string,
-  I extends ComponentInjectOptions = {},
-  II extends string = string,
-  S extends SlotsType = {},
+  InjectOptions extends ComponentInjectOptions = {},
+  InjectKeys extends string = string,
+  Slots extends SlotsType = {},
+  LocalComponents extends Record<string, Component> = {},
+  Directives extends Record<string, Directive> = {},
+  Exposed extends string = string,
+  Provide extends ComponentProvideOptions = ComponentProvideOptions,
+  // resolved types
+  InferredProps = string extends PropsKeys
+    ? ComponentObjectPropsOptions extends RuntimePropsOptions
+      ? {}
+      : ExtractPropTypes<RuntimePropsOptions>
+    : { [key in PropsKeys]?: any },
+  ResolvedProps = InferredProps & EmitsToProps<RuntimeEmitsOptions>,
 >(
-  options: ComponentOptionsWithoutProps<
-    Props,
-    RawBindings,
-    D,
-    C,
-    M,
+  options: {
+    props?: (RuntimePropsOptions & ThisType<void>) | PropsKeys[]
+  } & ComponentOptionsBase<
+    ResolvedProps,
+    SetupBindings,
+    Data,
+    Computed,
+    Methods,
     Mixin,
     Extends,
-    E,
-    EE,
-    I,
-    II,
-    S
-  > & { styles?: string[] },
-): VueElementConstructor<Props>
-
-// overload 3: object format with array props declaration
-export function defineCustomElement<
-  PropNames extends string,
-  RawBindings,
-  D,
-  C extends ComputedOptions = {},
-  M extends MethodOptions = {},
-  Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
-  Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
-  E extends EmitsOptions = Record<string, any>,
-  EE extends string = string,
-  I extends ComponentInjectOptions = {},
-  II extends string = string,
-  S extends SlotsType = {},
->(
-  options: ComponentOptionsWithArrayProps<
-    PropNames,
-    RawBindings,
-    D,
-    C,
-    M,
-    Mixin,
-    Extends,
-    E,
-    EE,
-    I,
-    II,
-    S
-  > & { styles?: string[] },
-): VueElementConstructor<{ [K in PropNames]: any }>
-
-// overload 4: object format with object props declaration
-export function defineCustomElement<
-  PropsOptions extends Readonly<ComponentPropsOptions>,
-  RawBindings,
-  D,
-  C extends ComputedOptions = {},
-  M extends MethodOptions = {},
-  Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
-  Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
-  E extends EmitsOptions = Record<string, any>,
-  EE extends string = string,
-  I extends ComponentInjectOptions = {},
-  II extends string = string,
-  S extends SlotsType = {},
->(
-  options: ComponentOptionsWithObjectProps<
-    PropsOptions,
-    RawBindings,
-    D,
-    C,
-    M,
-    Mixin,
-    Extends,
-    E,
-    EE,
-    I,
-    II,
-    S
-  > & { styles?: string[] },
-): VueElementConstructor<ExtractPropTypes<PropsOptions>>
+    RuntimeEmitsOptions,
+    EmitsKeys,
+    {}, // Defaults
+    InjectOptions,
+    InjectKeys,
+    Slots,
+    LocalComponents,
+    Directives,
+    Exposed,
+    Provide
+  > &
+    ThisType<
+      CreateComponentPublicInstance<
+        Readonly<ResolvedProps>,
+        SetupBindings,
+        Data,
+        Computed,
+        Methods,
+        Mixin,
+        Extends,
+        RuntimeEmitsOptions,
+        EmitsKeys,
+        {},
+        false,
+        InjectOptions,
+        Slots,
+        LocalComponents,
+        Directives,
+        Exposed
+      >
+    >,
+): VueElementConstructor<ResolvedProps>
 
 // overload 5: defining a custom element from the returned value of
 // `defineComponent`
index 63372d829169e273459201dfcee8832f99057a3a..4846751b84e9370f8db7ebe425cbf126c3c82899 100644 (file)
@@ -21,3 +21,34 @@ export type Awaited<T> = T extends null | undefined
       ? Awaited<V> // recursively unwrap the value
       : never // the argument to `then` was not callable
     : T // non-object or non-thenable
+
+/**
+ * Utility for extracting the parameters from a function overload (for typed emits)
+ * https://github.com/microsoft/TypeScript/issues/32164#issuecomment-1146737709
+ */
+export type OverloadParameters<T extends (...args: any[]) => any> = Parameters<
+  OverloadUnion<T>
+>
+
+type OverloadProps<TOverload> = Pick<TOverload, keyof TOverload>
+
+type OverloadUnionRecursive<
+  TOverload,
+  TPartialOverload = unknown,
+> = TOverload extends (...args: infer TArgs) => infer TReturn
+  ? TPartialOverload extends TOverload
+    ? never
+    :
+        | OverloadUnionRecursive<
+            TPartialOverload & TOverload,
+            TPartialOverload &
+              ((...args: TArgs) => TReturn) &
+              OverloadProps<TOverload>
+          >
+        | ((...args: TArgs) => TReturn)
+  : never
+
+type OverloadUnion<TOverload extends (...args: any[]) => any> = Exclude<
+  OverloadUnionRecursive<(() => never) & TOverload>,
+  TOverload extends () => never ? never : () => never
+>