Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(type): improve defineComponent type for option apis #406

Merged
merged 2 commits into from Jun 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions src/component/common.ts
@@ -0,0 +1 @@
export type Data = { [key: string]: unknown }
161 changes: 0 additions & 161 deletions src/component/component.ts

This file was deleted.

100 changes: 100 additions & 0 deletions src/component/componentOptions.ts
@@ -0,0 +1,100 @@
import { Data } from './common'
import { ComponentPropsOptions, ExtractPropTypes } from './componentProps'
import { VNode } from 'vue'
import { ComponentInstance, ComponentRenderProxy } from './componentProxy'

import { ComponentOptions as Vue2ComponentOptions } from 'vue'

export interface SetupContext {
readonly attrs: Record<string, string>
readonly slots: { [key: string]: (...args: any[]) => VNode[] }
readonly parent: ComponentInstance | null
readonly root: ComponentInstance
readonly listeners: { [key: string]: Function }

emit(event: string, ...args: any[]): void
}

export type ComputedGetter<T> = (ctx?: any) => T
export type ComputedSetter<T> = (v: T) => void

export interface WritableComputedOptions<T> {
get: ComputedGetter<T>
set: ComputedSetter<T>
}

export type ComputedOptions = Record<
string,
ComputedGetter<any> | WritableComputedOptions<any>
>

export interface MethodOptions {
[key: string]: Function
}

export type SetupFunction<Props, RawBindings> = (
this: void,
props: Props,
ctx: SetupContext
) => RawBindings | (() => VNode | null)

interface ComponentOptionsBase<
Props,
D = Data,
C extends ComputedOptions = {},
M extends MethodOptions = {}
>
extends Omit<
Vue2ComponentOptions<Vue, D, M, C, Props>,
'data' | 'computed' | 'method' | 'setup' | 'props'
> {
data?: (this: Props, vm: Props) => D
computed?: C
methods?: M
}

export type ExtractComputedReturns<T extends any> = {
[key in keyof T]: T[key] extends { get: (...args: any[]) => infer TReturn }
? TReturn
: T[key] extends (...args: any[]) => infer TReturn
? TReturn
: never
}

export type ComponentOptionsWithProps<
PropsOptions = ComponentPropsOptions,
RawBindings = Data,
D = Data,
C extends ComputedOptions = {},
M extends MethodOptions = {},
Props = ExtractPropTypes<PropsOptions>
> = ComponentOptionsBase<Props, D, C, M> & {
props?: PropsOptions
setup?: SetupFunction<Props, RawBindings>
} & ThisType<ComponentRenderProxy<Props, RawBindings, D, C, M>>

export type ComponentOptionsWithArrayProps<
PropNames extends string = string,
RawBindings = Data,
D = Data,
C extends ComputedOptions = {},
M extends MethodOptions = {},
Props = Readonly<{ [key in PropNames]?: any }>
> = ComponentOptionsBase<Props, D, C, M> & {
props?: PropNames[]
setup?: SetupFunction<Props, RawBindings>
} & ThisType<ComponentRenderProxy<Props, RawBindings, D, C, M>>

export type ComponentOptionsWithoutProps<
Props = unknown,
RawBindings = Data,
D = Data,
C extends ComputedOptions = {},
M extends MethodOptions = {}
> = ComponentOptionsBase<Props, D, C, M> & {
props?: undefined
setup?: SetupFunction<Props, RawBindings>
} & ThisType<ComponentRenderProxy<Props, RawBindings, D, C, M>>

export type WithLegacyAPI<T, D, C, M, Props> = T &
Omit<Vue2ComponentOptions<Vue, D, M, C, Props>, keyof T>
2 changes: 1 addition & 1 deletion src/component/componentProps.ts
@@ -1,4 +1,4 @@
import { Data } from './component'
import { Data } from './common'

export type ComponentPropsOptions<P = Data> =
| ComponentObjectPropsOptions<P>
Expand Down
69 changes: 69 additions & 0 deletions src/component/componentProxy.ts
@@ -0,0 +1,69 @@
import { ExtractPropTypes } from './componentProps'
import { UnwrapRef } from '..'
import { Data } from './common'

import Vue, {
VueConstructor,
ComponentOptions as Vue2ComponentOptions,
} from 'vue'
import {
ComputedOptions,
MethodOptions,
ExtractComputedReturns,
} from './componentOptions'

export type ComponentInstance = InstanceType<VueConstructor>

// public properties exposed on the proxy, which is used as the render context
// in templates (as `this` in the render option)
export type ComponentRenderProxy<
P = {}, // props type extracted from props option
B = {}, // raw bindings returned from setup()
D = {}, // return from data()
C extends ComputedOptions = {},
M extends MethodOptions = {},
PublicProps = P
> = {
$data: D
$props: Readonly<P & PublicProps>
$attrs: Data
$refs: Data
$slots: Data
$root: ComponentInstance | null
$parent: ComponentInstance | null
$emit: (event: string, ...args: unknown[]) => void
} & Readonly<P> &
UnwrapRef<B> &
D &
M &
ExtractComputedReturns<C> &
Vue

// for Vetur and TSX support
type VueConstructorProxy<PropsOptions, RawBindings> = VueConstructor & {
new (...args: any[]): ComponentRenderProxy<
ExtractPropTypes<PropsOptions>,
UnwrapRef<RawBindings>,
ExtractPropTypes<PropsOptions, false>
>
}

type DefaultData<V> = object | ((this: V) => object)
type DefaultMethods<V> = { [key: string]: (this: V, ...args: any[]) => any }
type DefaultComputed = { [key: string]: any }

export type VueProxy<
PropsOptions,
RawBindings,
Data = DefaultData<Vue>,
Computed = DefaultComputed,
Methods = DefaultMethods<Vue>
> = Vue2ComponentOptions<
Vue,
UnwrapRef<RawBindings> & Data,
Methods,
Computed,
PropsOptions,
ExtractPropTypes<PropsOptions, false>
> &
VueConstructorProxy<PropsOptions, RawBindings>