-
-
Notifications
You must be signed in to change notification settings - Fork 33.7k
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
Improve Vue type declarations for more canonical usage #5887
Changes from 15 commits
5b5a88f
385a744
8cd5b9c
540a38f
f34f4f6
b1f40ce
355ff75
bc54007
e7ea5bb
ebde0b1
d78d14b
fc83771
a50c838
3c86b10
33a106c
0f586db
1092efe
ebd8c0b
c628103
e4a8545
f7ebfa3
bb0ff30
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,37 +1,36 @@ | ||
import * as V from "./vue"; | ||
import * as Options from "./options"; | ||
import * as Plugin from "./plugin"; | ||
import * as VNode from "./vnode"; | ||
import { Vue } from "./vue"; | ||
|
||
// `Vue` in `export = Vue` must be a namespace | ||
// All available types are exported via this namespace | ||
declare namespace Vue { | ||
export type CreateElement = V.CreateElement; | ||
export default Vue; | ||
|
||
export type Component = Options.Component; | ||
export type AsyncComponent = Options.AsyncComponent; | ||
export type ComponentOptions<V extends Vue> = Options.ComponentOptions<V>; | ||
export type FunctionalComponentOptions = Options.FunctionalComponentOptions; | ||
export type RenderContext = Options.RenderContext; | ||
export type PropOptions = Options.PropOptions; | ||
export type ComputedOptions<V extends Vue> = Options.ComputedOptions<V>; | ||
export type WatchHandler<V extends Vue> = Options.WatchHandler<V, any>; | ||
export type WatchOptions = Options.WatchOptions; | ||
export type DirectiveFunction = Options.DirectiveFunction; | ||
export type DirectiveOptions = Options.DirectiveOptions; | ||
export { | ||
CreateElement | ||
} from "./vue"; | ||
|
||
export type PluginFunction<T> = Plugin.PluginFunction<T>; | ||
export type PluginObject<T> = Plugin.PluginObject<T>; | ||
export { | ||
Component, | ||
AsyncComponent, | ||
ComponentOptions, | ||
FunctionalComponentOptions, | ||
RenderContext, | ||
PropOptions, | ||
ComputedOptions, | ||
WatchHandler, | ||
WatchOptions, | ||
WatchOptionsWithHandler, | ||
DirectiveFunction, | ||
DirectiveOptions | ||
} from "./options"; | ||
|
||
export type VNodeChildren = VNode.VNodeChildren; | ||
export type VNodeChildrenArrayContents = VNode.VNodeChildrenArrayContents; | ||
export type VNode = VNode.VNode; | ||
export type VNodeComponentOptions = VNode.VNodeComponentOptions; | ||
export type VNodeData = VNode.VNodeData; | ||
export type VNodeDirective = VNode.VNodeDirective; | ||
} | ||
export { | ||
PluginFunction, | ||
PluginObject | ||
} from "./plugin"; | ||
|
||
// TS cannot merge imported class with namespace, declare a subclass to bypass | ||
declare class Vue extends V.Vue {} | ||
|
||
export = Vue; | ||
export { | ||
VNodeChildren, | ||
VNodeChildrenArrayContents, | ||
VNode, | ||
VNodeComponentOptions, | ||
VNodeData, | ||
VNodeDirective | ||
} from "./vnode"; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,43 +1,82 @@ | ||
import { Vue, CreateElement } from "./vue"; | ||
import { Vue, CreateElement, CombinedVueInstance } from "./vue"; | ||
import { VNode, VNodeData, VNodeDirective } from "./vnode"; | ||
|
||
type Constructor = { | ||
new (...args: any[]): any; | ||
} | ||
|
||
export type Component = typeof Vue | ComponentOptions<Vue> | FunctionalComponentOptions; | ||
export type AsyncComponent = ( | ||
resolve: (component: Component) => void, | ||
export type Component<Data, Methods, Computed, PropNames extends string = never> = | ||
typeof Vue | | ||
FunctionalOrStandardComponentOptions<Data, Methods, Computed, PropNames>; | ||
|
||
export type AsyncComponent<Data, Methods, Computed, PropNames extends string> = ( | ||
resolve: (component: Component<Data, Methods, Computed, PropNames>) => void, | ||
reject: (reason?: any) => void | ||
) => Promise<Component> | Component | void; | ||
) => Promise<Component<Data, Methods, Computed, PropNames>> | Component<Data, Methods, Computed, PropNames> | void; | ||
|
||
/** | ||
* When the `Computed` type parameter on `ComponentOptions` is inferred, | ||
* it should have a property with the return type of every get-accessor. | ||
* Since there isn't a way to query for the return type of a function, we allow TypeScript | ||
* to infer from the shape of `Accessors<Computed>` and work backwards. | ||
*/ | ||
export type Accessors<T> = { | ||
[K in keyof T]: (() => T[K]) | ComputedOptions<T[K]> | ||
} | ||
|
||
export interface ComponentOptions<V extends Vue> { | ||
data?: Object | ((this: V) => Object); | ||
props?: string[] | { [key: string]: PropOptions | Constructor | Constructor[] }; | ||
/** | ||
* This type should be used when an array of strings is used for a component's `props` value. | ||
*/ | ||
export type ThisTypedComponentOptionsWithArrayProps<Data, Methods, Computed, PropNames extends string> = | ||
object & | ||
ComponentOptions<Data | ((this: Record<PropNames, any> & Vue) => Data), Methods, Computed, PropNames[]> & | ||
ThisType<CombinedVueInstance<Data, Methods, Computed, Record<PropNames, any>>>; | ||
|
||
/** | ||
* This type should be used when an object mapped to `PropOptions` is used for a component's `props` value. | ||
*/ | ||
export type ThisTypedComponentOptionsWithRecordProps<Data, Methods, Computed, Props> = | ||
object & | ||
ComponentOptions<Data | ((this: Record<keyof Props, any> & Vue) => Data), Methods, Computed, Props> & | ||
ThisType<CombinedVueInstance<Data, Methods, Computed, Props>>; | ||
|
||
/** | ||
* A helper type that describes options for either functional or non-functional components. | ||
* Useful for `Vue.extend` and `Vue.component`. | ||
*/ | ||
export type FunctionalOrStandardComponentOptions<Data, Methods, Computed, PropNames extends string = never> = | ||
| FunctionalComponentOptions<PropNames[] | Record<PropNames, PropValidator>, Record<PropNames, any>> | ||
| ThisTypedComponentOptionsWithArrayProps<Data, Methods, Computed, PropNames> | ||
| ThisTypedComponentOptionsWithRecordProps<Data, Methods, Computed, Record<PropNames, PropOptions>>; | ||
|
||
|
||
export interface ComponentOptions<Data, Methods, Computed, Props> { | ||
data?: Data; | ||
props?: Props; | ||
propsData?: Object; | ||
computed?: { [key: string]: ((this: V) => any) | ComputedOptions<V> }; | ||
methods?: { [key: string]: (this: V, ...args: any[]) => any }; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We might also need I haven't try it myself, but will it cause cyclic reference? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There are a few problems that potentially come up if you try to reference
@HerringtonDarkholme that's not actually necessary. When a method needs its You should definitely try it out! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tried it locally. It works like charm! Sorry for the wrong reporting. Vetur's issue is caused by |
||
watch?: { [key: string]: ({ handler: WatchHandler<V, any> } & WatchOptions) | WatchHandler<V, any> | string }; | ||
computed?: Accessors<Computed>; | ||
methods?: Methods; | ||
watch?: Record<string, WatchOptionsWithHandler<any> | WatchHandler<any> | string>; | ||
|
||
el?: Element | String; | ||
template?: string; | ||
render?(this: V, createElement: CreateElement): VNode; | ||
render?(createElement: CreateElement): VNode; | ||
renderError?: (h: () => VNode, err: Error) => VNode; | ||
staticRenderFns?: ((createElement: CreateElement) => VNode)[]; | ||
|
||
beforeCreate?(this: V): void; | ||
created?(this: V): void; | ||
beforeDestroy?(this: V): void; | ||
destroyed?(this: V): void; | ||
beforeMount?(this: V): void; | ||
mounted?(this: V): void; | ||
beforeUpdate?(this: V): void; | ||
updated?(this: V): void; | ||
activated?(this: V): void; | ||
deactivated?(this: V): void; | ||
|
||
directives?: { [key: string]: DirectiveOptions | DirectiveFunction }; | ||
components?: { [key: string]: Component | AsyncComponent }; | ||
beforeCreate?(): void; | ||
created?(): void; | ||
beforeDestroy?(): void; | ||
destroyed?(): void; | ||
beforeMount?(): void; | ||
mounted?(): void; | ||
beforeUpdate?(): void; | ||
updated?(): void; | ||
activated?(): void; | ||
deactivated?(): void; | ||
|
||
directives?: { [key: string]: DirectiveFunction | DirectiveOptions }; | ||
components?: { [key: string]: Component<any, any, any, never> | AsyncComponent<any, any, any, never> }; | ||
transitions?: { [key: string]: Object }; | ||
filters?: { [key: string]: Function }; | ||
|
||
|
@@ -50,48 +89,55 @@ export interface ComponentOptions<V extends Vue> { | |
}; | ||
|
||
parent?: Vue; | ||
mixins?: (ComponentOptions<Vue> | typeof Vue)[]; | ||
mixins?: (ComponentOptions<any, any, any, any> | typeof Vue)[]; | ||
name?: string; | ||
extends?: ComponentOptions<Vue> | typeof Vue; | ||
// TODO: support properly inferred 'extends' | ||
extends?: ComponentOptions<any, any, any, any> | typeof Vue; | ||
delimiters?: [string, string]; | ||
} | ||
|
||
export interface FunctionalComponentOptions { | ||
export interface FunctionalComponentOptions<Props = object, ContextProps = object> { | ||
name?: string; | ||
props?: string[] | { [key: string]: PropOptions | Constructor | Constructor[] }; | ||
props?: Props; | ||
functional: boolean; | ||
render(this: never, createElement: CreateElement, context: RenderContext): VNode | void; | ||
render(this: undefined, createElement: CreateElement, context: RenderContext<ContextProps>): VNode; | ||
} | ||
|
||
export interface RenderContext { | ||
props: any; | ||
export interface RenderContext<Props> { | ||
props: Props; | ||
children: VNode[]; | ||
slots(): any; | ||
data: VNodeData; | ||
parent: Vue; | ||
injections: any | ||
} | ||
|
||
export type PropValidator = PropOptions | Constructor | Constructor[]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we have any chance to infer There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I originally tried to do that but the more I thought about it, the more time I realized it'd take to finish. I figured it'd be better to put the PR out and try that at a later stage. |
||
|
||
export interface PropOptions { | ||
type?: Constructor | Constructor[] | null; | ||
required?: boolean; | ||
default?: any; | ||
default?: string | number | boolean | null | undefined | (() => object); | ||
validator?(value: any): boolean; | ||
} | ||
|
||
export interface ComputedOptions<V> { | ||
get?(this: V): any; | ||
set?(this: V, value: any): void; | ||
export interface ComputedOptions<T> { | ||
get?(): T; | ||
set?(value: T): void; | ||
cache?: boolean; | ||
} | ||
|
||
export type WatchHandler<V, T> = (this: V, val: T, oldVal: T) => void; | ||
export type WatchHandler<T> = (val: T, oldVal: T) => void; | ||
|
||
export interface WatchOptions { | ||
deep?: boolean; | ||
immediate?: boolean; | ||
} | ||
|
||
export interface WatchOptionsWithHandler<T> extends WatchOptions { | ||
handler: WatchHandler<T>; | ||
} | ||
|
||
export type DirectiveFunction = ( | ||
el: HTMLElement, | ||
binding: VNodeDirective, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't it be depending on a stable version?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It depended on some experimental changes but afaik they landed in 2.4, didn't they @DanielRosenwasser?