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('<slot w/ nested component>', () => {
-      const { slots } = parseWithSlots(
-        `<Comp><Comp><slot/></Comp></Comp>`
-      )
+      const { slots } = parseWithSlots(`<Comp><Comp><slot/></Comp></Comp>`)
       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<string, unknown>,
+  Emits extends EmitsOptions = {},
+  S = {},
+  LC extends Record<string, Component> = {},
+  LD extends Record<string, Directive> = {},
   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<string, any>,
-  EE extends string = string,
-  PP = PublicProps,
-  Props = Readonly<ExtractPropTypes<PropsOrPropOptions>> & EmitsToProps<E>,
-  Defaults = ExtractDefaultPropTypes<PropsOrPropOptions>
-> = 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<Props, Emits, S, {}, LC, LD, D, Options>
 
 // 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<Props, RawBindings = object>(
+export function defineComponent<
+  Props extends Record<string, unknown> = {},
+  RawBindings = {},
+  Emits extends EmitsOptions = {},
+  S = {},
+  Exposed extends string = string
+>(
   setup: (
     props: Readonly<Props>,
-    ctx: SetupContext
+    ctx: SetupContext<Emits, Slots<S>>
   ) => RawBindings | RenderFunction
-): DefineComponent<Props, RawBindings>
+): 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<string, Component> = {},
+  LD extends Record<string, Directive> = {},
   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<Props, RawBindings, D, C, M, Mixin, Extends, E, EE>
-
+): 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<string, Component> = {},
+  LD extends Record<string, Directive> = {},
+  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<string, any>,
-  EE extends string = string
+  Defaults = {},
+  Props extends Record<string, unknown> = 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<ComponentPropsOptions>,
-  RawBindings,
-  D,
+  PropsOptions extends ComponentObjectPropsOptions = ComponentObjectPropsOptions,
+  Emits extends EmitsOptions = {},
+  S = {},
+  LC extends Record<string, Component> = {},
+  LD extends Record<string, Directive> = {},
+  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<string, any>,
-  EE extends string = string
+  Defaults = ExtractDefaultPropTypes<PropsOptions>,
+  Props extends Record<string, unknown> = Readonly<
+    ExtractPropTypes<PropsOptions>
+  >
 >(
-  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<PropsOptions, RawBindings, D, C, M, Mixin, Extends, E, EE>
+>
 
 // 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<InternalSlots> = {
+export const legacySlotProxyHandlers: ProxyHandler<
+  InternalSlots<Record<string, null>>
+> = {
   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<string, unknown>
  */
 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> {}
+
 /**
  * Default allowed non-declared props on component in TSX
  */
@@ -103,10 +141,13 @@ export interface ComponentInternalOptions {
   __file?: string
 }
 
-export interface FunctionalComponent<P = {}, E extends EmitsOptions = {}>
-  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<SetupContext<E>, 'expose'>): any
+  (props: P, ctx: Omit<SetupContext<E, P, S>, 'expose'>): any
   props?: ComponentPropsOptions<P>
   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<Props, RawBindings, D, C, M>
-  | FunctionalComponent<Props, any>
+  | ComponentOptions<Props, RawBindings, D, C, M, Mixin, Extends, E>
+  | FunctionalComponent<Props, E>
 
 /**
  * A type used in public APIs where a component type is expected.
@@ -148,6 +192,43 @@ export type Component<
   | ConcreteComponent<Props, RawBindings, D, C, M>
   | ComponentPublicInstanceConstructor<Props>
 
+// export type BetterConcreteComponent<
+//   Props extends Record<string, Prop<unknown>> = {},
+//   Emits extends EmitsOptions = {},
+//   Slots = {},
+//   // Allows to expose public API properties, this bypasses Data/Computed/Methods,
+//   // easier to declare manually
+//   Bindings extends Record<string, unknown> = {},
+//   Options extends ComponentOptions = {}
+
+// > =;
+
+export type BetterComponent<
+  Props extends Record<string, unknown> = {},
+  Emits extends EmitsOptions = {},
+  Slots = {},
+  // Allows to expose public API properties, this bypasses Data/Computed/Methods,
+  // easier to declare manually
+  Bindings extends Record<string, unknown> = {},
+  LocalComponents extends Record<string, Component> = {},
+  LocalDirectives extends Record<string, Directive> = {},
+  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<Props, Bindings>
+  | ComponentPublicInstanceConstructor<Props>
+  | { supa: Options }
+
+const ddd: BetterComponent = {
+  setup() {}
+}
+
 export { ComponentOptions }
 
 type LifecycleHook<TFn = Function> = TFn[] | null
@@ -169,10 +250,14 @@ export const enum LifecycleHooks {
   SERVER_PREFETCH = 'sp'
 }
 
-export interface SetupContext<E = EmitsOptions> {
+export interface SetupContext<
+  E = EmitsOptions,
+  P = {},
+  S extends Slots = Slots
+> {
   attrs: Data
-  slots: Slots
-  emit: EmitFn<E>
+  slots: S
+  emit: EmitFn<E, P>
   expose: (exposed?: Record<string, any>) => 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 EmitsOptions> = T extends string[]
     }
   : {}
 
+// declare function processEmits<T extends EmitsOptions>(a: T): EmitsToProps<T>
+
+// const x = processEmits({ click: (a: 1) => true })
+
+// declare const a: EmitsToProps<'click'[] & 'bar'[]>
+// a.onClick
+
+// type Unpacked<T> = T extends (infer U)[]
+//   ? U
+//   : T extends (...args: any[]) => infer U
+//   ? U
+//   : T extends Promise<infer U>
+//   ? U
+//   : T
+// type SSS<T> = 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<infer V>
+> = (Options extends Array<infer V>
   ? (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<P>
 
 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> = T extends ComponentOptionsBase<
   any, // M
   any, // Mixin
   any, // Extends
-  any // EmitsOptions
+  infer E // EmitsOptions
 >
   ? unknown extends P
     ? {}
-    : P
+    : P & EmitsToProps<E>
   : {}
 
+export interface BetterComponentOptionsBase<
+  Props extends Record<string, unknown>,
+  Emits extends EmitsOptions,
+  S,
+  LC extends Record<string, Component>,
+  LD extends Record<string, Directive>,
+  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<Props, D, C, M, Mixin, Extends, Emits>,
+    ComponentInternalOptions,
+    ComponentCustomOptions {
+  setup?: (
+    this: void,
+    props: Readonly<
+      LooseRequired<
+        Props &
+          UnionToIntersection<ExtractOptionProp<Mixin>> &
+          UnionToIntersection<ExtractOptionProp<Extends>> &
+          EmitsToProps<Emits>
+      >
+    >,
+    ctx: SetupContext<Emits, Props, Slots<S>>
+  ) => Promise<RawBindings> | 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<void>) | EE[]
+  expose?: Exposed[]
+  serverPrefetch?(): Promise<any>
+
+  // Runtime compiler only -----------------------------------------------------
+  compilerOptions?: RuntimeCompilerOptions
+
+  slots?: S & ThisType<void>
+
+  // 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<ConcreteComponent>
+  /**
+   * 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<string, Component> = {},
+  Directives extends Record<string, Directive> = {},
+  Exposed extends string = string,
   Defaults = {}
-> extends LegacyOptions<Props, D, C, M, Mixin, Extends>,
+> extends LegacyOptions<Props, D, C, M, Mixin, Extends, E>,
     ComponentInternalOptions,
     ComponentCustomOptions {
   setup?: (
@@ -125,10 +236,11 @@ export interface ComponentOptionsBase<
       LooseRequired<
         Props &
           UnionToIntersection<ExtractOptionProp<Mixin>> &
-          UnionToIntersection<ExtractOptionProp<Extends>>
+          UnionToIntersection<ExtractOptionProp<Extends>> &
+          EmitsToProps<E>
       >
     >,
-    ctx: SetupContext<E>
+    ctx: SetupContext<E, Props, Slots<S>>
   ) => Promise<RawBindings> | 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<string, Component>
-  directives?: Record<string, Directive>
+  components?: LC
+  directives?: Directives
   inheritAttrs?: boolean
-  emits?: (E | EE[]) & ThisType<void>
-  // TODO infer public instance type based on exposed keys
-  expose?: string[]
+  emits?: (E & ThisType<void>) | EE[]
+  expose?: Exposed[]
   serverPrefetch?(): Promise<any>
 
   // Runtime compiler only -----------------------------------------------------
   compilerOptions?: RuntimeCompilerOptions
 
+  slots?: S & ThisType<void>
+
   // 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<string, Component> = {},
+  Directives extends Record<string, Directive> = {},
+  Exposed extends string = string,
   PE = Props & EmitsToProps<E>
 > = ComponentOptionsBase<
   PE,
@@ -233,11 +350,31 @@ export type ComponentOptionsWithoutProps<
   Extends,
   E,
   EE,
+  S,
+  LC,
+  Directives,
+  Exposed,
   {}
 > & {
   props?: undefined
 } & ThisType<
-    CreateComponentPublicInstance<PE, RawBindings, D, C, M, Mixin, Extends, E>
+    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<E>
+  S = any,
+  LC extends Record<string, Component> = {},
+  Directives extends Record<string, Directive> = {},
+  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<ExtractPropTypes<PropsOptions>> & EmitsToProps<E>,
+  S = any,
+  LC extends Record<string, Component> = {},
+  Directives extends Record<string, Directive> = {},
+  Exposed extends string = string,
+  Props = Readonly<ExtractPropTypes<PropsOptions>>,
   Defaults = ExtractDefaultPropTypes<PropsOptions>
 > = ComponentOptionsBase<
   Props,
@@ -299,6 +455,10 @@ export type ComponentOptionsWithObjectProps<
   Extends,
   E,
   EE,
+  S,
+  LC,
+  Directives,
+  Exposed,
   Defaults
 > & {
   props: PropsOptions & ThisType<void>
@@ -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<Props, RawBindings, D, C, M, Mixin, Extends, E> &
+  E extends EmitsOptions = {},
+  S = any,
+  LC extends Record<string, Component> = {},
+  Directives extends Record<string, Directive> = {},
+  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<Props>
+      S,
+      Props,
+      {},
+      false,
+      LC,
+      Directives,
+      Exposed
+    >
+  >
+
+export type BettterComponentOptionsWithoutProps<
+  Emits extends EmitsOptions = {},
+  S = {},
+  LC extends Record<string, Component> = {},
+  LD extends Record<string, Directive> = {},
+  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<string, unknown> = {},
+  Emits extends EmitsOptions = {},
+  S = {},
+  LC extends Record<string, Component> = {},
+  LD extends Record<string, Directive> = {},
+  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<string, unknown> = {},
+  Emits extends EmitsOptions = {},
+  S = {},
+  LC extends Record<string, Component> = {},
+  LD extends Record<string, Directive> = {},
+  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<void>
+}
+
+// export type BetterComponentOptionsTyped<
+//   Props extends Record<string, unknown> = {},
+//   Emits extends EmitsOptions = {},
+//   S = {},
+//   LC extends Record<string, Component> = {},
+//   LD extends Record<string, Directive> = {},
+//   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<string, unknown> = {},
+  Emits extends EmitsOptions = {},
+  S = {},
+  LC extends Record<string, Component> = {},
+  LD extends Record<string, Directive> = {},
+  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<ErrorCapturedHook>
 }
 
-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<string, unknown> = {},
+  E extends EmitsOptions = {},
+  LC extends Record<string, Component> = {},
+  LD extends Record<string, Directive> = {},
+  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<T> = {
     : never
 }[keyof T]
 
-type InferPropType<T> = [T] extends [null]
+export type InferPropType<T> = [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> = [T] extends [null]
     : V
   : T
 
-export type ExtractPropTypes<O> = O extends object
+export type ExtractPropTypesOld<O> = 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<O>]: InferPropType<O[K]> } &
       { [K in OptionalKeys<O>]?: InferPropType<O[K]> }
   : { [K in string]: any }
 
+export type ExtractPropTypes<O, RK = RequiredKeys<O>> = O extends object
+  ? {
+      [K in keyof O]: K extends RK
+        ? InferPropType<O[K]>
+        : InferPropType<O[K]> | 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> = T extends ComponentOptionsBase<
   infer M,
   infer Mixin,
   infer Extends,
+  infer E,
+  any,
+  any,
+  any,
   any,
   any,
   infer Defaults
 >
-  ? OptionTypesType<P & {}, B & {}, D & {}, C & {}, M & {}, Defaults & {}> &
+  ? OptionTypesType<P & {}, B & {}, D & {}, C & {}, M & {}, E, Defaults & {}> &
       IntersectionMixin<Mixin> &
       IntersectionMixin<Extends>
   : never
 
+type BetterMixinToOptionTypes<T> = 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<Mixin> &
+      BetterIntersectionMixin<Extends>
+  : never
+
 // ExtractMixin(map type) is used to resolve circularly references
 type ExtractMixin<T> = {
   Mixin: MixinToOptionTypes<T>
 }[T extends ComponentOptionsMixin ? 'Mixin' : never]
 
 type IntersectionMixin<T> = IsDefaultMixinComponent<T> extends true
-  ? OptionTypesType<{}, {}, {}, {}, {}>
+  ? OptionTypesType<{}, {}, {}, {}, {}, {}, {}>
   : UnionToIntersection<ExtractMixin<T>>
 
+type BetterExtractMixin<T> = {
+  Mixin: MixinToOptionTypes<T>
+}[T extends ComponentOptionsMixin ? 'Mixin' : never]
+type BetterIntersectionMixin<T> = IsDefaultMixinComponent<T> extends true
+  ? BetterOptionTypesType<{}, {}, {}, {}, {}, {}, {}>
+  : BetterExtractMixin<T>
+
+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> = T extends void ? {} : T
 
+/**
+  Props extends Record<string, unknown> = {},
+  Emits extends EmitsOptions = {},
+  S extends Slots<unknown> = {},
+  Bindings extends Record<string, unknown> = {},
+  LC extends Record<string, Component> = {},
+  LD extends Record<string, Directive> = {},
+  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<string, unknown> = {},
+  Emits extends EmitsOptions = {},
+  S = {},
+  // Allows to expose public API properties, this bypasses Data/Computed/Methods,
+  // easier to declare manually
+  Bindings extends Record<string, unknown> = {},
+  LC extends Record<string, Component> = {},
+  LD extends Record<string, Directive> = {},
+  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<Slots<S>>,
+    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 EmitsOptions> = T extends string[]
+  ? // the omit serves to remove the type 'bar[]' from `bar[] & { foo: Function}`
+    Record<T[number], null> &
+      (T extends object ? Omit<T, T[number] | keyof string[]> : never)
+  : T
+
+export type BetterCreateComponentPublicInstance<
+  P extends Record<string, unknown> = {},
+  E extends EmitsOptions = {},
+  S = any,
+  LC extends Record<string, Component> = {},
+  LD extends Record<string, Directive> = {},
+  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<Mixin> &
+    BetterIntersectionMixin<Extends>,
+  PublicP extends Record<string, unknown> = Readonly<
+    BetterUnwrapMixinsType<PublicMixin, 'P'>
+  > &
+    EnsureNonVoid<P>,
+  PublicLC extends Record<string, Component> = BetterUnwrapMixinsType<
+    PublicMixin,
+    'LC'
+  > &
+    EnsureNonVoid<LC>,
+  PublicLD extends Record<string, Directive> = BetterUnwrapMixinsType<
+    PublicMixin,
+    'LD'
+  > &
+    EnsureNonVoid<LD>,
+  PublicB = BetterUnwrapMixinsType<PublicMixin, 'B'> & EnsureNonVoid<B>,
+  PublicD = BetterUnwrapMixinsType<PublicMixin, 'D'> & EnsureNonVoid<D>,
+  PublicC extends ComputedOptions = BetterUnwrapMixinsType<PublicMixin, 'C'> &
+    EnsureNonVoid<C>,
+  PublicM extends MethodOptions = BetterUnwrapMixinsType<PublicMixin, 'M'> &
+    EnsureNonVoid<M>,
+  // Emits behave a bit different and require union instead of intersection
+  PublicE extends EmitsOptions = FixS<
+    BetterUnwrapMixinsType<BetterIntersectionMixin<Mixin>, 'E'>
+  > &
+    FixS<BetterUnwrapMixinsType<BetterIntersectionMixin<Extends>, 'E'>> &
+    FixS<EnsureNonVoid<E>>,
+  PublicDefaults = BetterUnwrapMixinsType<PublicMixin, 'Defaults'> &
+    EnsureNonVoid<Defaults>
+> = BetterComponentPublicInstance<
+  (MakeDefaultsOptional extends true
+    ? Partial<PublicDefaults> &
+        Omit<PublicP & PublicProps, keyof PublicDefaults>
+    : PublicP & PublicProps) &
+    EmitsToProps<PublicE>,
+  PublicE,
+  Readonly<Slots<S>>,
+  ExposedKeys<
+    // Props
+    (MakeDefaultsOptional extends true
+      ? Partial<PublicDefaults> &
+          Omit<PublicP & PublicProps, keyof PublicDefaults>
+      : PublicP & PublicProps) &
+      EmitsToProps<PublicE> &
+      // /Props
+
+      ShallowUnwrapRef<PublicB> &
+      UnwrapNestedRefs<PublicD> &
+      ExtractComputedReturns<PublicC> &
+      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<string, Component> = {},
+  Directives extends Record<string, Directive> = {},
+  Exposed extends string = string,
   PublicMixin = IntersectionMixin<Mixin> & IntersectionMixin<Extends>,
-  PublicP = UnwrapMixinsType<PublicMixin, 'P'> & EnsureNonVoid<P>,
+  PublicP = Readonly<UnwrapMixinsType<PublicMixin, 'P'>> & EnsureNonVoid<P>,
   PublicB = UnwrapMixinsType<PublicMixin, 'B'> & EnsureNonVoid<B>,
   PublicD = UnwrapMixinsType<PublicMixin, 'D'> & EnsureNonVoid<D>,
   PublicC extends ComputedOptions = UnwrapMixinsType<PublicMixin, 'C'> &
     EnsureNonVoid<C>,
   PublicM extends MethodOptions = UnwrapMixinsType<PublicMixin, 'M'> &
     EnsureNonVoid<M>,
+  // Emits behave a bit different and require union instead of intersection
+  PublicE extends EmitsOptions = FixS<
+    UnwrapMixinsType<IntersectionMixin<Mixin>, 'E'>
+  > &
+    FixS<UnwrapMixinsType<IntersectionMixin<Extends>, 'E'>> &
+    FixS<EnsureNonVoid<E>>,
   PublicDefaults = UnwrapMixinsType<PublicMixin, 'Defaults'> &
     EnsureNonVoid<Defaults>
 > = ComponentPublicInstance<
@@ -157,13 +364,78 @@ export type CreateComponentPublicInstance<
   PublicD,
   PublicC,
   PublicM,
-  E,
+  PublicE,
+  S,
   PublicProps,
   PublicDefaults,
   MakeDefaultsOptional,
-  ComponentOptionsBase<P, B, D, C, M, Mixin, Extends, E, string, Defaults>
+  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<T, Exposed>
+
+export type BetterComponentPublicInstance<
+  Props extends Record<string, unknown> = {},
+  Emits extends EmitsOptions = {},
+  S extends Slots<unknown> = {},
+  Bindings extends Record<string, unknown> = {},
+  LC extends Record<string, Component> = {},
+  LD extends Record<string, Directive> = {},
+  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<S>
+  $root: BetterComponentPublicInstance | null
+  $parent: BetterComponentPublicInstance | null
+  $emit: EmitFn<Emits, Props>
+  $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<any, any, any, any, any, any, any, any, any>
+  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<Defaults> & Omit<P & PublicProps, keyof Defaults>
-    : P & PublicProps
+    : P & PublicProps) &
+    EmitsToProps<E>
   $attrs: Data
   $refs: Data
-  $slots: Slots
+  $slots: Slots<S>
   $root: ComponentPublicInstance | null
   $parent: ComponentPublicInstance | null
-  $emit: EmitFn<E>
+  $emit: EmitFn<E, P>
   $el: any
   $options: Options & MergedComponentOptionsOverride
   $forceUpdate: () => void
@@ -198,12 +485,15 @@ export type ComponentPublicInstance<
     cb: Function,
     options?: WatchOptions
   ): WatchStopHandle
-} & P &
-  ShallowUnwrapRef<B> &
-  UnwrapNestedRefs<D> &
-  ExtractComputedReturns<C> &
-  M &
-  ComponentCustomProperties
+} & ExposedKeys<
+  P &
+    ShallowUnwrapRef<B> &
+    UnwrapNestedRefs<D> &
+    ExtractComputedReturns<C> &
+    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> = T extends null
+  ? () => VNode[] | VNode
+  : (arg: T) => VNode[] | VNode
+
+export type InternalSlots<T = any> = {
+  [K in keyof T]?: T[K] extends () => infer R ? SlotTyped<R> : SlotTyped<T[K]>
 }
 
-export type Slots = Readonly<InternalSlots>
+export type SlotsObject<T = any> = InternalSlots<T>
+export type SlotArray<V = PropertyKey> = V extends PropertyKey
+  ? Record<V, Slot>
+  : Record<string, Slot>
 
-export type RawSlots = {
-  [name: string]: unknown
+export type Slots<T = any> = RenderSlot &
+  (unknown extends T
+    ? Readonly<Partial<Record<string, Slot>>>
+    : T extends Array<infer V>
+    ? Readonly<SlotArray<V>>
+    : Readonly<SlotsObject<T>>)
+
+declare const rr: Slots<{ a: null }> extends Readonly<InternalSlots<any>>
+  ? 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<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
 
@@ -43,25 +53,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>
 
 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<P = any> {
+interface Constructor<P = {}, S = any> {
   __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> = T extends Readonly<Array<infer V>>
+  ? { [K in V & string as `on${Capitalize<K>}`]: (...args: any[]) => void }
+  : T extends any[]
+  ? { [K in T & string as `on${Capitalize<K>}`]: (...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<K>}`]: 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<Array<infer V>>
+  ? { [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<P, `on${Capitalize<PK>}`> &
+        string as `onUpdate:${K}`]?: (value: P[K]) => void
+    }
+
+type RenderProps<P, E extends EmitsOptions = {}> =
+  | (Partial<ExtractEmitEvents<E>> & RawProps & P & ExtractEmitPropUpdate<P>)
+  | ({} extends P ? Partial<ExtractEmitEvents<E>> | null : never)
+
+type RenderSlots<S> =
+  | Slots<S>
+  | ({} extends S ? RawSlots : Slots<S>)
+  | 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<P, E extends EmitsOptions = {}>(
+  type: FunctionalComponent<P, E>,
+  props?: RenderProps<P, E>,
+  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<P, E extends EmitsOptions = {}>(
-  type: FunctionalComponent<P, E>,
-  props?: (RawProps & P) | ({} extends P ? null : never),
-  children?: RawChildren | RawSlots
-): VNode
+// export function h<P, E extends EmitsOptions = {}>(
+//   type: FunctionalComponent<P, E>,
+//   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<P>(
-  type: ConcreteComponent | string,
+export function h<P, E extends EmitsOptions = {}>(
+  type: ConcreteComponent<P, any, any, any, any, any, any, E> | string,
+  props?: RenderProps<P, E>,
   children?: RawChildren
 ): VNode
+
 export function h<P>(
-  type: ConcreteComponent<P> | 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<P>(
-  type: ComponentOptions<P>,
-  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<P>(
-  type: Constructor<P>,
-  props?: (RawProps & P) | ({} extends P ? null : never),
-  children?: RawChildren | RawSlots
+export function h<
+  P,
+  S,
+  E extends EmitsOptions = {},
+  PP = {},
+  Props = {},
+  Defaults = {}
+>(
+  type: ComponentOptions<P, any, any, any, any, any, any, E, S>,
+  props?: RenderProps<Partial<Defaults> & Omit<Props & PP, keyof Defaults>, E>,
+  children?: (RawChildren & Slots<S>) | ({} extends S ? RawSlots : Slots<S>)
 ): 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<Partial<Defaults> & Omit<Props & PP, keyof Defaults>, E>,
+  children?: (RawChildren & Slots<S>) | ({} extends S ? RawSlots : Slots<S>)
+): VNode
+export function h(type: DefineComponent): VNode
 export function h(type: DefineComponent, children?: RawChildren): VNode
-export function h<P>(
-  type: DefineComponent<P>,
-  props?: (RawProps & P) | ({} extends P ? null : never),
-  children?: RawChildren | RawSlots
+
+// fake constructor type returned by `defineComponent` or class component
+export function h<P, S, E extends EmitsOptions = {}>(
+  type: Constructor<P, S>,
+  props?: RenderProps<P, E>,
+  children?: (RawChildren & Slots<S>) | ({} extends S ? RawSlots : Slots<S>)
 ): 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<string, unknown>,
+//   E extends EmitsOptions,
+//   S
+// >(
+//   type: BetterComponentOptions<P, E, S>,
+//   props?: RenderProps<P, E>,
+//   children?: (RawChildren & Slots<S>) | ({} extends S ? RawSlots : Slots<S>)
+// ): P
+export function betterH<
+  P extends Record<string, unknown>,
+  E extends EmitsOptions,
+  S
+>(
+  type: BetterComponent<P, E, S>,
+  props?: RenderProps<P, E>,
+  children?: RenderSlots<S>
+): P
+
+export function betterH<
+  P extends Record<string, unknown>,
+  E extends EmitsOptions,
+  S
+>(
+  type: RenderComponent<P, E, S>,
+  props?: RenderProps<P, E>,
+  children?: RenderSlots<S>
+): 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<U> = (
   : never
 
 // make keys required but keep undefined values
-export type LooseRequired<T> = { [P in string & keyof T]: T[P] }
+// export type LooseRequired<T> = { [P in string & keyof T]: T[P] }
+// TODO validate this change, was what's above
+export type LooseRequired<T> = { [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<TeleportProps>
+    Suspense: DefineComponent<SuspenseProps>
+    KeepAlive: DefineComponent<KeepAliveProps>
+    BaseTransition: DefineComponent<BaseTransitionProps>
+  }
+}
+
 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<TeleportProps>
+    Suspense: DefineComponent<SuspenseProps>
+    KeepAlive: DefineComponent<KeepAliveProps>
+    BaseTransition: DefineComponent<BaseTransitionProps>
+  }
+}
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<T> = ObjectDirective<T & { _assign: AssignerFn }>
+type ModelDirective<T, Modifiers extends string = string> = 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<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) {
@@ -327,3 +332,10 @@ if (__NODE_JS__) {
     }
   }
 }
+
+export type VModelDirective =
+  | typeof vModelText
+  | typeof vModelCheckbox
+  | typeof vModelSelect
+  | typeof vModelRadio
+  | typeof vModelDynamic
diff --git a/packages/runtime-dom/src/directives/vOn.ts b/packages/runtime-dom/src/directives/vOn.ts
index 06e596266cf..533b180ace9 100644
--- a/packages/runtime-dom/src/directives/vOn.ts
+++ b/packages/runtime-dom/src/directives/vOn.ts
@@ -3,36 +3,44 @@ import {
   DeprecationTypes,
   LegacyConfig,
   compatUtils,
-  ComponentInternalInstance
+  ComponentInternalInstance,
+  Directive
 } from '@vue/runtime-core'
 import { hyphenate, isArray } from '@vue/shared'
 
-const systemModifiers = ['ctrl', 'shift', 'alt', 'meta']
-
 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,
-  exact: (e, modifiers) =>
+type SystemModifiers = 'ctrl' | 'shift' | 'alt' | 'meta'
+type CompatModifiers = keyof typeof keyNames
+
+export type VOnModifiers = SystemModifiers | ModifierGuards | CompatModifiers
+
+const systemModifiers: Array<SystemModifiers> = ['ctrl', 'shift', 'alt', 'meta']
+
+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: Event, modifiers: string[]) =>
     systemModifiers.some(m => (e as any)[`${m}Key`] && !modifiers.includes(m))
 }
 
+type ModifierGuards = keyof typeof modifierGuards
+
 /**
  * @private
  */
-export const withModifiers = (fn: Function, modifiers: string[]) => {
+export const withModifiers = (
+  fn: Function,
+  modifiers: Array<ModifierGuards | SystemModifiers>
+) => {
   return (event: Event, ...args: unknown[]) => {
     for (let i = 0; i < modifiers.length; i++) {
       const guard = modifierGuards[modifiers[i]]
@@ -44,7 +52,7 @@ export const withModifiers = (fn: Function, modifiers: string[]) => {
 
 // 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',
@@ -83,7 +91,11 @@ export const withKeys = (fn: Function, modifiers: string[]) => {
     }
 
     const eventKey = hyphenate(event.key)
-    if (modifiers.some(k => k === eventKey || keyNames[k] === eventKey)) {
+    if (
+      modifiers.some(
+        k => k === eventKey || keyNames[k as CompatModifiers] === eventKey
+      )
+    ) {
       return fn(event)
     }
 
@@ -114,3 +126,5 @@ export const withKeys = (fn: Function, modifiers: string[]) => {
     }
   }
 }
+
+export type VOnDirective = Directive<any, any, VOnModifiers>
diff --git a/packages/runtime-dom/src/index.ts b/packages/runtime-dom/src/index.ts
index ed35e1293f8..ef1fd05aa91 100644
--- a/packages/runtime-dom/src/index.ts
+++ b/packages/runtime-dom/src/index.ts
@@ -9,6 +9,8 @@ import {
   App,
   RootHydrateFunction,
   isRuntimeOnly,
+  DefineComponent,
+  Directive,
   DeprecationTypes,
   compatUtils
 } from '@vue/runtime-core'
@@ -16,6 +18,11 @@ import { nodeOps } from './nodeOps'
 import { patchProp } from './patchProp'
 // Importing from the compiler, will be tree-shaken in prod
 import { isFunction, isString, isHTMLTag, isSVGTag, extend } from '@vue/shared'
+import { TransitionProps } from './components/Transition'
+import { TransitionGroupProps } from './components/TransitionGroup'
+import { vShow } from './directives/vShow'
+import { VOnDirective } from './directives/vOn'
+import { VModelDirective } from './directives/vModel'
 
 declare module '@vue/reactivity' {
   export interface RefUnwrapBailTypes {
@@ -24,6 +31,24 @@ declare module '@vue/reactivity' {
   }
 }
 
+declare module '@vue/runtime-core' {
+  interface GlobalComponents {
+    // Note: if updating this, also update `types/globalComponents.d.ts`.
+    Transition: DefineComponent<TransitionProps>
+    TransitionGroup: DefineComponent<TransitionGroupProps>
+  }
+
+  interface GlobalDirectives {
+    // Note: if updating this, also update `types/globalDirectives.d.ts`.
+    vShow: typeof vShow
+    vOn: VOnDirective
+    vBind: VModelDirective
+    vIf: Directive<any, boolean>
+    VOnce: Directive
+    VSlot: Directive
+  }
+}
+
 const rendererOptions = extend({ patchProp }, nodeOps)
 
 // lazy create the renderer - this makes core renderer logic tree-shakable
diff --git a/packages/runtime-dom/types/globalComponents.d.ts b/packages/runtime-dom/types/globalComponents.d.ts
new file mode 100644
index 00000000000..28d1bfc87c4
--- /dev/null
+++ b/packages/runtime-dom/types/globalComponents.d.ts
@@ -0,0 +1,10 @@
+// Note: this file is auto concatenated to the end of the bundled d.ts during
+// build.
+
+import { DefineComponent } from '@vue/runtime-core'
+declare module '@vue/runtime-core' {
+  interface GlobalComponents {
+    Transition: DefineComponent<TransitionProps>
+    TransitionGroup: DefineComponent<TransitionGroupProps>
+  }
+}
diff --git a/packages/runtime-dom/types/globalDirectives.d.ts b/packages/runtime-dom/types/globalDirectives.d.ts
new file mode 100644
index 00000000000..041d85e90fb
--- /dev/null
+++ b/packages/runtime-dom/types/globalDirectives.d.ts
@@ -0,0 +1,8 @@
+// Note: this file is auto concatenated to the end of the bundled d.ts during
+// build.
+
+declare module '@vue/runtime-core' {
+  interface GlobalDirectives {
+    vShow: typeof vShow
+  }
+}
diff --git a/packages/server-renderer/__tests__/render.spec.ts b/packages/server-renderer/__tests__/render.spec.ts
index 1079a29a6c2..b40c4802004 100644
--- a/packages/server-renderer/__tests__/render.spec.ts
+++ b/packages/server-renderer/__tests__/render.spec.ts
@@ -856,11 +856,11 @@ function testRender(type: string, render: typeof renderToString) {
         await render(
           createApp({
             components: {
-              A: {
+              A: defineComponent({
                 ssrRender(_ctx, _push) {
                   _push(`<div>A</div>`)
                 }
-              },
+              }),
               B: {
                 render: () => h('div', 'B')
               }
diff --git a/packages/vue-compat/__tests__/instance.spec.ts b/packages/vue-compat/__tests__/instance.spec.ts
index 93ceda93c54..08029fb8b43 100644
--- a/packages/vue-compat/__tests__/instance.spec.ts
+++ b/packages/vue-compat/__tests__/instance.spec.ts
@@ -239,7 +239,8 @@ test('INSTANCE_LISTENERS', () => {
     components: {
       child: {
         template: `<div/>`,
-        mounted() {
+        mounted(this: LegacyPublicInstance) {
+          // @ts-expect-error $listeners type: Record<string, Function | Function[]>
           listeners = this.$listeners
         }
       }
@@ -263,7 +264,7 @@ describe('INSTANCE_SCOPED_SLOTS', () => {
       components: {
         child: {
           compatConfig: { RENDER_FUNCTION: false },
-          render() {
+          render(this: LegacyPublicInstance) {
             slots = this.$scopedSlots
           }
         }
@@ -290,11 +291,14 @@ describe('INSTANCE_SCOPED_SLOTS', () => {
       components: {
         child: {
           compatConfig: { RENDER_FUNCTION: false },
-          render() {
+          render(this: LegacyPublicInstance) {
             normalSlots = this.$slots
             scopedSlots = this.$scopedSlots
           }
         }
+      },
+      render() {
+        this.$
       }
     }).$mount()
 
diff --git a/test-dts/component.test-d.ts b/test-dts/component.test-d.ts
index 93c3ea4109c..75e8556cc00 100644
--- a/test-dts/component.test-d.ts
+++ b/test-dts/component.test-d.ts
@@ -459,6 +459,21 @@ describe('functional', () => {
 
     expectType<number>(props.foo)
   })
+
+  describe('emit', () => {
+    const MyComponent: FunctionalComponent<
+      { foo: number },
+      { ev: (a: string) => void }
+    > = (_, { emit }) => {
+      emit('ev', 'a')
+      // @ts-expect-error invalid argument
+      emit('ev', 12)
+      // @ts-expect-error invalid event
+      emit('o', '1')
+    }
+
+    defineComponent(MyComponent)
+  })
 })
 
 declare type VueClass<Props = {}> = {
diff --git a/test-dts/componentTypeExtensions.test-d.tsx b/test-dts/componentTypeExtensions.test-d.tsx
index 32a72f844e6..46da0724f9c 100644
--- a/test-dts/componentTypeExtensions.test-d.tsx
+++ b/test-dts/componentTypeExtensions.test-d.tsx
@@ -1,10 +1,24 @@
-import { defineComponent, expectError, expectType } from './index'
+import {
+  defineComponent,
+  DefineComponent,
+  Directive,
+  expectError,
+  expectType
+} from './index'
 
 declare module '@vue/runtime-core' {
   interface ComponentCustomOptions {
     test?(n: number): void
   }
 
+  interface GlobalDirectives {
+    test: Directive
+  }
+
+  interface GlobalComponents {
+    RouterView: DefineComponent<{}>
+  }
+
   interface ComponentCustomProperties {
     state: 'stopped' | 'running'
   }
@@ -41,6 +55,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} />)
diff --git a/test-dts/defineComponent.test-d.tsx b/test-dts/defineComponent.test-d.tsx
index 9569b585f50..7de755b18eb 100644
--- a/test-dts/defineComponent.test-d.tsx
+++ b/test-dts/defineComponent.test-d.tsx
@@ -11,8 +11,12 @@ import {
   ComponentPublicInstance,
   ComponentOptions,
   SetupContext,
-  IsUnion,
-  h
+  h,
+  Directive,
+  KeepAliveProps,
+  TransitionProps,
+  vShow,
+  IsUnion
 } from './index'
 
 describe('with object props', () => {
@@ -551,7 +555,7 @@ describe('with mixins', () => {
       return {}
     },
 
-    setup(props) {
+    setup(props, _) {
       expectType<string>(props.z)
       // props
       expectType<((...args: any[]) => any) | undefined>(props.onClick)
@@ -697,6 +701,8 @@ describe('with extends', () => {
 describe('extends with mixins', () => {
   const Mixin = defineComponent({
     emits: ['bar'],
+    // emits: { bar: (a: string) => true },
+
     props: {
       mP1: {
         type: String,
@@ -709,6 +715,7 @@ describe('extends with mixins', () => {
       }
     },
     data() {
+      this.$props.onBar
       return {
         a: 1
       }
@@ -734,14 +741,31 @@ describe('extends with mixins', () => {
     },
     computed: {
       c(): number {
+        this.$XXX
         return this.p2 + this.b
       }
     }
   })
+  const OtherMixin = defineComponent({
+    emits: {
+      test: (a: boolean) => true
+    },
+
+    render() {
+      this.$emit('test', true)
+
+      // @ts-expect-error
+      this.$emit('test', 0)
+
+      // @ts-expect-error
+      this.$emit('')
+    }
+  })
+
   const MyComponent = defineComponent({
     extends: Base,
-    mixins: [Mixin],
-    emits: ['click'],
+    mixins: [Mixin, OtherMixin],
+    emits: ['click', 'lock'],
     props: {
       // required should make property non-void
       z: {
@@ -751,6 +775,19 @@ describe('extends with mixins', () => {
     },
     render() {
       const props = this.$props
+
+      //@ts-expect-error
+      this.$emit('')
+
+      this.$emit('bar')
+      this.$emit('foo')
+      this.$emit('click')
+
+      this.$emit('test', true)
+
+      //@ts-expect-error
+      this.$emit('test', 0)
+
       // props
       expectType<((...args: any[]) => any) | undefined>(props.onClick)
       // from Mixin
@@ -1054,7 +1091,8 @@ describe('extract instance type', () => {
         required: true
       },
       c: Number
-    }
+    },
+    setup(props) {}
   })
 
   const compA = {} as InstanceType<typeof CompA>
@@ -1115,6 +1153,148 @@ describe('async setup', () => {
   vm.a = 2
 })
 
+describe('typed slots', () => {
+  const Comp = defineComponent({
+    slots: {
+      test: null,
+      item: Object as () => { item: { value: number }; i: number }
+    },
+
+    setup(_, { slots }) {
+      slots.test!()
+      slots.item!({
+        i: 22,
+        item: {
+          value: 22
+        }
+      })
+      // @ts-expect-error missing item prop
+      expectError(slots.item!({ i: 22 }))
+    }
+  })
+
+  h(
+    // @ts-expect-error the provided slots are not valid
+    Comp,
+    {},
+    {
+      // @ts-expect-error no argument expected
+      test(x) {},
+      item(s) {
+        expectType<number>(s.i)
+        expectType<{ value: number }>(s.item)
+        return {} as any
+      }
+    }
+  )
+})
+
+describe('typed slots just type', () => {
+  const Comp = defineComponent({
+    slots: {} as {
+      test: null
+      item: { item: { value: number }; i: number }
+    },
+
+    setup(_, { slots }) {
+      slots.test!()
+      slots.item!({
+        i: 22,
+        item: {
+          value: 22
+        }
+      })
+      // @ts-expect-error missing item prop
+      expectError(slots.item!({ i: 22 }))
+    }
+  })
+
+  h(
+    // @ts-expect-error the provided slots are not valid
+    Comp,
+    {},
+    {
+      // @ts-expect-error no argument expected
+      test(x) {},
+      item(s) {
+        expectType<number>(s.i)
+        expectType<{ value: number }>(s.item)
+
+        return {} as any
+      }
+    }
+  )
+})
+
+// #3367 expose components types
+describe('expose component types', () => {
+  const child = defineComponent({
+    props: {
+      a: String
+    }
+  })
+
+  const parent = defineComponent({
+    components: {
+      child
+    }
+  })
+
+  expectType<typeof child>(parent.components!.child)
+
+  // global components
+  expectType<KeepAliveProps>(new parent.components!.KeepAlive().$props)
+  expectType<KeepAliveProps>(new child.components!.KeepAlive().$props)
+
+  // runtime-dom components
+  expectType<TransitionProps>(new parent.components!.Transition().$props)
+  expectType<TransitionProps>(new parent.components!.Transition().$props)
+})
+
+describe('directive typing', () => {
+  const customDirective: Directive = {
+    created(el) {}
+  }
+
+  const comp = defineComponent({
+    props: {
+      a: String
+    },
+    directives: {
+      customDirective
+    }
+  })
+
+  expectType<typeof customDirective>(comp.directives!.customDirective)
+
+  // 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 }
+    }
+  })
+
+  expect<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
+})
+
 // check if defineComponent can be exported
 export default {
   // function components
diff --git a/test-dts/directives.test-d.ts b/test-dts/directives.test-d.ts
new file mode 100644
index 00000000000..700c9727292
--- /dev/null
+++ b/test-dts/directives.test-d.ts
@@ -0,0 +1,58 @@
+import { ObjectDirective } from '@vue/runtime-core'
+import { Directive, expectError, expectType, vModelText } from './index'
+
+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, 'trim' | 'number', string>>(vModelText)
+})
+
+describe('custom', () => {
+  expectType<{
+    value: number
+    oldValue: number | null
+    arg?: 'Arg'
+    modifiers: Record<'a' | 'b', boolean>
+  }>(testDirective<number, 'a' | 'b', 'Arg'>())
+
+  expectError<{
+    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'>())
+})
diff --git a/test-dts/functionalComponent.test-d.tsx b/test-dts/functionalComponent.test-d.tsx
index fdcf346fb70..5f650ff3d37 100644
--- a/test-dts/functionalComponent.test-d.tsx
+++ b/test-dts/functionalComponent.test-d.tsx
@@ -29,12 +29,19 @@ const Bar: FunctionalComponent<
   expectType<number>(props.foo)
 
   emit('update', 123)
+
+  emit('update:foo', 123)
+
   //  @ts-expect-error
   expectError(emit('nope'))
   //  @ts-expect-error
   expectError(emit('update'))
   //  @ts-expect-error
   expectError(emit('update', 'nope'))
+  // @ts-expect-error
+  expectError(emit('update:foo'))
+  // @ts-expect-error
+  expectError(emit('update:foo', 'nope'))
 }
 
 // assigning runtime options
diff --git a/test-dts/h.test-d.ts b/test-dts/h.test-d.ts
index c71b54a2aa8..f5bfd20bbbd 100644
--- a/test-dts/h.test-d.ts
+++ b/test-dts/h.test-d.ts
@@ -9,7 +9,9 @@ import {
   Component,
   expectError,
   expectAssignable,
-  resolveComponent
+  FunctionalComponent,
+  resolveComponent,
+  expectType
 } from './index'
 
 describe('h inference w/ element', () => {
@@ -70,12 +72,29 @@ describe('h inference w/ functional component', () => {
   const Func = (_props: { foo: string; bar?: number }) => ''
   h(Func, { foo: 'hello' })
   h(Func, { foo: 'hello', bar: 123 })
+
+  h(Func, {
+    foo: '',
+    'onUpdate:bar'(v) {
+      expectType<number | undefined>(v)
+    },
+    'onUpdate:foo'(v) {
+      expectType<string>(v)
+    }
+  })
+
   //  @ts-expect-error
   expectError(h(Func, { foo: 123 }))
   //  @ts-expect-error
   expectError(h(Func, {}))
   //  @ts-expect-error
   expectError(h(Func, { bar: 123 }))
+
+  const Func2: FunctionalComponent<{}, ['foo', 'bar']> = () => {}
+  h(Func2, { onFoo() {}, onBar() {} })
+
+  // @ts-expect-error
+  h(Func2, { onFoo: 1 })
 })
 
 describe('h support w/ plain object component', () => {
@@ -84,9 +103,36 @@ describe('h support w/ plain object component', () => {
       foo: String
     }
   }
-  h(Foo, { foo: 'ok' })
-  h(Foo, { foo: 'ok', class: 'extra' })
+  h(Foo, {
+    foo: 'ok',
+    'onUpdate:foo'(v) {
+      expectType<string>(v)
+    }
+  })
+  h(Foo, {
+    foo: 'ok',
+    class: 'extra',
+
+    'onUpdate:foo'(v) {
+      expectType<string>(v)
+    }
+  })
+
   // no inference in this case
+  h(
+    {
+      emits: {
+        foo(a: number) {
+          return true
+        }
+      }
+    },
+    {
+      onFoo(s) {
+        expectType<number>(s)
+      }
+    }
+  )
 })
 
 describe('h inference w/ defineComponent', () => {
@@ -104,12 +150,61 @@ describe('h inference w/ defineComponent', () => {
   h(Foo, { bar: 1, foo: 'ok' })
   // should allow extraneous props (attrs fallthrough)
   h(Foo, { bar: 1, foo: 'ok', class: 'extra' })
+
+  // should support model
+  h(Foo, {
+    bar: 1,
+    'onUpdate:bar'(v) {
+      expectType<number>(v)
+    },
+    'onUpdate:foo'(v) {
+      expectType<string | undefined>(v)
+    }
+  })
+
   // @ts-expect-error should fail on missing required prop
   expectError(h(Foo, {}))
   //  @ts-expect-error
   expectError(h(Foo, { foo: 'ok' }))
   // @ts-expect-error should fail on wrong type
   expectError(h(Foo, { bar: 1, foo: 1 }))
+
+  const FooEmit = defineComponent({
+    props: {
+      foo: String
+    },
+    emits: {
+      foo(a: number) {
+        return true
+      }
+    }
+  })
+
+  h(FooEmit, {
+    onFoo(a) {
+      expectType<number>(a)
+    }
+  })
+
+  const BarPropEmit = defineComponent({
+    props: {
+      bar: Number
+    },
+    emits: {
+      bar(a: number) {
+        return true
+      }
+    }
+  })
+
+  h(BarPropEmit, {
+    onBar(a) {
+      expectType<number>(a)
+    },
+    'onUpdate:bar'(a) {
+      expectType<number | undefined>(a)
+    }
+  })
 })
 
 // describe('h inference w/ defineComponent + optional props', () => {
diff --git a/test-dts/index.d.ts b/test-dts/index.d.ts
index 3d8d288fa6d..6d9d88eaf45 100644
--- a/test-dts/index.d.ts
+++ b/test-dts/index.d.ts
@@ -9,8 +9,8 @@ export function expectType<T>(value: T): void
 export function expectError<T>(value: T): void
 export function expectAssignable<T, T2 extends T = T>(value: T2): void
 
-export type IsUnion<T, U extends T = T> = (T extends any
-  ? (U extends T ? false : true)
-  : never) extends false
+export type IsUnion<T, U extends T = T> = (
+  T extends any ? (U extends T ? false : true) : never
+) extends false
   ? false
   : true
diff --git a/test-dts/tsx.test-d.tsx b/test-dts/tsx.test-d.tsx
index ce1aec82043..74b1c1bdb89 100644
--- a/test-dts/tsx.test-d.tsx
+++ b/test-dts/tsx.test-d.tsx
@@ -1,4 +1,5 @@
 // TSX w/ defineComponent is tested in defineComponent.test-d.tsx
+import { betterDefineComponent } from 'packages/runtime-core/src/apiDefineComponent'
 import {
   KeepAlive,
   Suspense,
@@ -65,3 +66,7 @@ expectType<JSX.Element>(
 )
 // @ts-expect-error
 expectError(<Suspense onResolve={123} />)
+
+const EmptyComponent = betterDefineComponent({ props: {} })
+
+;<EmptyComponent />
diff --git a/yarn.lock b/yarn.lock
index 53dc72e8960..1dde7ff3c86 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7242,7 +7242,12 @@ typedarray@^0.0.6:
   resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
   integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
 
-typescript@^4.2.2, typescript@~4.3.5:
+typescript@^4.4.2:
+  version "4.4.2"
+  resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.2.tgz#6d618640d430e3569a1dfb44f7d7e600ced3ee86"
+  integrity sha512-gzP+t5W4hdy4c+68bfcv0t400HVJMMd2+H9B7gae1nQlBzCqvrXX+6GL/b3GAgyTH966pzrZ70/fRjwAtZksSQ==
+
+typescript@~4.3.5:
   version "4.3.5"
   resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4"
   integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==