Skip to content

Commit

Permalink
types: use ComponentOptions Types from vue itself to better type the …
Browse files Browse the repository at this point in the history
…main function
  • Loading branch information
LinusBorg committed Nov 5, 2022
1 parent babada7 commit e45e6b0
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 109 deletions.
106 changes: 79 additions & 27 deletions src/createComposable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,33 +18,88 @@ import {
ref,
computed,
watch,
type ComponentPublicInstance,
type WatchCallback,
type ToRefs,
reactive,
provide,
type ComponentPublicInstance,
type WatchCallback,
} from 'vue'
import { callHook, isArray, isFunction, isObject } from './utils'
import { createContextProxy } from './vmContextProxy'

import type {
ComputedOptions,
MethodOptions,
ComponentOptionsWithObjectProps,
ComponentOptionsMixin,
ToRefs,
CreateComponentPublicInstance,
ExtractPropTypes,
ComponentPropsOptions,
ExtractDefaultPropTypes,
EmitsOptions,
} from 'vue'

// import { cache } from './cache'

import type {
ComponentWatchOptionItem,
Mixin,
ExtractComputedReturns,
MethodOptions,
ComputedOptions,
EmitsToProps,
} from './types'
import { resolveInjections } from './inject'

export function createComposableFromMixin<
TData extends Record<string, any>,
TMethods extends MethodOptions,
TComputed extends ComputedOptions,
Props extends Record<string, any> | string[],
Emits extends string[]
>(mixin: Mixin<TData, TMethods, TComputed, Props, Emits>) {
export /* @__PURE__ */ function createComposableFromMixin<
Props extends Readonly<ExtractPropTypes<PropsOptions>> & EmitsToProps<E>,
VM extends CreateComponentPublicInstance<
Props,
RawBindings,
D,
C,
M,
Mixin,
Extends,
E,
Props,
ExtractDefaultPropTypes<PropsOptions>,
false
>,
PropsOptions extends Readonly<ComponentPropsOptions>,
RawBindings,
D,
C extends ComputedOptions = {},
M extends MethodOptions = {},
Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
E extends EmitsOptions = {},
EE extends string = string
>(
mixin: ComponentOptionsWithObjectProps<
PropsOptions,
RawBindings,
D,
C,
M,
Mixin,
Extends,
E,
EE
>
): () => ToRefs<D> & ToRefs<ExtractComputedReturns<C>> & M {
// type Props = Readonly<ExtractPropTypes<PropsOptions>> & EmitsToProps<E>
// type VM = CreateComponentPublicInstance<
// Props,
// RawBindings,
// D,
// C,
// M,
// Mixin,
// Extends,
// E,
// Props,
// ExtractDefaultPropTypes<PropsOptions>,
// false
// >

const {
props,
emits,
Expand Down Expand Up @@ -77,10 +132,7 @@ export function createComposableFromMixin<

const composable = () => {
const instance = getCurrentInstance()!
const vm = instance.proxy! as ComponentPublicInstance &
TData &
TMethods &
TComputed
const vm = instance.proxy! as VM

// if (mixins) {
// mixins.forEach((mixin) => {
Expand All @@ -90,12 +142,9 @@ export function createComposableFromMixin<
// })
// }

const context: Record<string, any> = {}
const context = {} as ToRefs<D> & ToRefs<ExtractComputedReturns<C>> & M
const reactiveContext = reactive(context)
const vmContextProxy = createContextProxy(
vm,
reactiveContext
) as ComponentPublicInstance
const vmContextProxy = createContextProxy(vm, reactiveContext) as VM

beforeCreate && callHook(beforeCreate, instance, 'bc')

Expand All @@ -113,8 +162,11 @@ export function createComposableFromMixin<

// data
if (dataFn) {
const data = dataFn.call(vmContextProxy, vmContextProxy)
// FIXME - any has to go here
const data = dataFn.call(vmContextProxy as any, vmContextProxy as any)
for (const key in data) {
// FIXME - TS `this`error
// @ts-expect-error this doesn't quite match yet
context[key] = ref(data[key])
}
}
Expand All @@ -130,6 +182,8 @@ export function createComposableFromMixin<
get: def.get.bind(vmContextProxy),
set: def.set.bind(vmContextProxy),
})
// FIXME - TS `this`error
// @ts-expect-error this doesn't quite match yet
context[key] = c
}
}
Expand Down Expand Up @@ -170,16 +224,14 @@ export function createComposableFromMixin<
renderTriggered && onRenderTriggered(renderTriggered.bind(vm))
errorCaptured && onErrorCaptured(errorCaptured.bind(vm))

return context as ToRefs<TData> &
TMethods &
ToRefs<ExtractComputedReturns<TComputed>>
return context
}

Object.assign(composable, {
props,
emits,
})
return composable as typeof composable & { props: Props; emits: Emits }
return composable as typeof composable & { props: Props; emits: E }
}

function createWatcher(
Expand Down
23 changes: 21 additions & 2 deletions src/inject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,27 @@
* https://github.com/vuejs/core/blob/f67bb500b6071bc0e55a89709a495a27da73badd/packages/runtime-core/src/componentOptions.ts#L1073-L1084
*/

import { inject, isRef, type Ref } from 'vue'
import type { ComponentInjectOptions, ObjectInjectOptions } from './types'
import {
inject,
isRef,
type ComponentPublicInstance,
type DebuggerEvent,
type Ref,
} from 'vue'

export type ComponentInjectOptions = string[] | ObjectInjectOptions

export type ObjectInjectOptions = Record<
string | symbol,
string | symbol | { from?: string | symbol; default?: unknown }
>
export type DebuggerHook = (e: DebuggerEvent) => void
export type ErrorCapturedHook<TError = unknown> = (
err: TError,
instance: ComponentPublicInstance | null,
info: string
) => boolean | void

import { isArray, isObject } from './utils'

export function resolveInjections(
Expand Down
101 changes: 21 additions & 80 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,33 @@

// TODO: replace with ComponentOptions or something
import type {
ComponentProvideOptions,
ComponentPublicInstance,
ComputedGetter,
DebuggerEvent,
EmitsOptions,
ObjectEmitsOptions,
WatchCallback,
WatchOptions,
WritableComputedOptions,
} from 'vue'
type TData = Record<string, unknown>

export type EmitsToProps<T extends EmitsOptions> = T extends string[]
? {
[K in string & `on${Capitalize<T[number]>}`]?: (...args: any[]) => any
}
: T extends ObjectEmitsOptions
? {
[K in string &
`on${Capitalize<string & keyof T>}`]?: K extends `on${infer C}`
? T[Uncapitalize<C>] extends null
? (...args: any[]) => any
: (
...args: T[Uncapitalize<C>] extends (...args: infer P) => any
? P
: never
) => any
: never
}
: {}

export interface MethodOptions<Context = ComponentPublicInstance> {
[key: string]: (
Expand Down Expand Up @@ -43,85 +61,8 @@ export type ExtractComputedReturns<T extends any> = {
: never
}

export type ComponentInjectOptions = string[] | ObjectInjectOptions

export type ObjectInjectOptions = Record<
string | symbol,
string | symbol | { from?: string | symbol; default?: unknown }
>
export type DebuggerHook = (e: DebuggerEvent) => void
export type ErrorCapturedHook<TError = unknown> = (
err: TError,
instance: ComponentPublicInstance | null,
info: string
) => boolean | void

export type ObjectWatchOptionItem = {
handler: WatchCallback | string
} & WatchOptions
type WatchOptionItem = string | WatchCallback | ObjectWatchOptionItem
export type ComponentWatchOptionItem = WatchOptionItem | WatchOptionItem[]

export type Mixin<
D = TData,
M = MethodOptions,
C = ComputedOptions,
Props = TData | string[],
Emits = string[],
Context = D & M & ExtractComputedReturns<C>,
MM = MethodOptions<Context>,
CC = ContextualizedComputedOptions<Context>
> = {
props?: Props
emits?: Emits

mixins?: Array<Record<string, any>>

data?: (vm?: ComponentPublicInstance) => D
methods?: MM
computed?: CC
watch?: Record<
string,
WatchCallback | ({ handler: WatchCallback } & WatchOptions)
>

provide?: ComponentProvideOptions
inject?: string[] | ObjectInjectOptions

beforeCreate?(): void
created?(): void
beforeMount?(): void
mounted?(): void
beforeUpdate?(): void
updated?(): void
activated?(): void
deactivated?(): void
/** @deprecated use `beforeUnmount` instead */
beforeDestroy?(): void
beforeUnmount?(): void
/** @deprecated use `unmounted` instead */
destroyed?(): void
unmounted?(): void
renderTracked?: DebuggerHook
renderTriggered?: DebuggerHook
errorCaptured?: ErrorCapturedHook
}
export type Composable = (...args: unknown[]) => Record<string, any>

type Options<T = TData> = {
data(): T
name?: string
}

function test<Data extends TData>(obj: Options<Data>): Data {
return obj.data()
}

const res = test({
data() {
return { a: 1 }
},
name: 'Bob',
})

res.a

0 comments on commit e45e6b0

Please sign in to comment.