Skip to content

Commit 105e040

Browse files
HerringtonDarkholmehefeng
authored and
hefeng
committed
feat(types): further improve Vue type declarations for canonical usage (vuejs#6391)
1 parent 20b735a commit 105e040

10 files changed

+394
-157
lines changed

package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@
7979
"eslint": "^3.0.0",
8080
"eslint-loader": "^1.7.1",
8181
"eslint-plugin-flowtype": "^2.34.0",
82-
"eslint-plugin-jasmine": "^2.2.0",
82+
"eslint-plugin-jasmine": "^2.8.4",
8383
"eslint-plugin-vue-libs": "^1.2.0",
8484
"file-loader": "^0.11.2",
8585
"flow-bin": "^0.54.0",
@@ -119,7 +119,7 @@
119119
"selenium-server": "^2.53.1",
120120
"serialize-javascript": "^1.3.0",
121121
"shelljs": "^0.7.8",
122-
"typescript": "^2.3.4",
122+
"typescript": "^2.5.2",
123123
"uglify-js": "^3.0.15",
124124
"webpack": "^2.6.1",
125125
"weex-js-runtime": "^0.22.0"

types/index.d.ts

+31-32
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,36 @@
1-
import * as V from "./vue";
2-
import * as Options from "./options";
3-
import * as Plugin from "./plugin";
4-
import * as VNode from "./vnode";
1+
import { Vue } from "./vue";
52

6-
// `Vue` in `export = Vue` must be a namespace
7-
// All available types are exported via this namespace
8-
declare namespace Vue {
9-
export type CreateElement = V.CreateElement;
3+
export default Vue;
104

11-
export type Component = Options.Component;
12-
export type AsyncComponent = Options.AsyncComponent;
13-
export type ComponentOptions<V extends Vue> = Options.ComponentOptions<V>;
14-
export type FunctionalComponentOptions = Options.FunctionalComponentOptions;
15-
export type RenderContext = Options.RenderContext;
16-
export type PropOptions = Options.PropOptions;
17-
export type ComputedOptions<V extends Vue> = Options.ComputedOptions<V>;
18-
export type WatchHandler<V extends Vue> = Options.WatchHandler<V, any>;
19-
export type WatchOptions = Options.WatchOptions;
20-
export type DirectiveFunction = Options.DirectiveFunction;
21-
export type DirectiveOptions = Options.DirectiveOptions;
5+
export {
6+
CreateElement
7+
} from "./vue";
228

23-
export type PluginFunction<T> = Plugin.PluginFunction<T>;
24-
export type PluginObject<T> = Plugin.PluginObject<T>;
9+
export {
10+
Component,
11+
AsyncComponent,
12+
ComponentOptions,
13+
FunctionalComponentOptions,
14+
RenderContext,
15+
PropOptions,
16+
ComputedOptions,
17+
WatchHandler,
18+
WatchOptions,
19+
WatchOptionsWithHandler,
20+
DirectiveFunction,
21+
DirectiveOptions
22+
} from "./options";
2523

26-
export type VNodeChildren = VNode.VNodeChildren;
27-
export type VNodeChildrenArrayContents = VNode.VNodeChildrenArrayContents;
28-
export type VNode = VNode.VNode;
29-
export type VNodeComponentOptions = VNode.VNodeComponentOptions;
30-
export type VNodeData = VNode.VNodeData;
31-
export type VNodeDirective = VNode.VNodeDirective;
32-
}
24+
export {
25+
PluginFunction,
26+
PluginObject
27+
} from "./plugin";
3328

34-
// TS cannot merge imported class with namespace, declare a subclass to bypass
35-
declare class Vue extends V.Vue {}
36-
37-
export = Vue;
29+
export {
30+
VNodeChildren,
31+
VNodeChildrenArrayContents,
32+
VNode,
33+
VNodeComponentOptions,
34+
VNodeData,
35+
VNodeDirective
36+
} from "./vnode";

types/options.d.ts

+92-37
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,88 @@
1-
import { Vue, CreateElement } from "./vue";
1+
import { Vue, CreateElement, CombinedVueInstance } from "./vue";
22
import { VNode, VNodeData, VNodeDirective } from "./vnode";
33

44
type Constructor = {
55
new (...args: any[]): any;
66
}
77

8-
export type Component = typeof Vue | ComponentOptions<Vue> | FunctionalComponentOptions;
8+
// we don't support infer props in async component
9+
export type Component<Data=DefaultData<Vue>, Methods=DefaultMethods<Vue>, Computed=DefaultComputed, Props=DefaultProps> =
10+
| typeof Vue
11+
| FunctionalComponentOptions<Props>
12+
| ThisTypedComponentOptionsWithArrayProps<Vue, Data, Methods, Computed, keyof Props>
13+
| ThisTypedComponentOptionsWithRecordProps<Vue, Data, Methods, Computed, Props>;
914

1015
interface EsModuleComponent {
1116
default: Component
1217
}
1318

14-
export type AsyncComponent = (
15-
resolve: (component: Component) => void,
19+
export type AsyncComponent<Data=DefaultData<Vue>, Methods=DefaultMethods<Vue>, Computed=DefaultComputed, Props=DefaultProps> = (
20+
resolve: (component: Component<Data, Methods, Computed, Props>) => void,
1621
reject: (reason?: any) => void
17-
) => Promise<Component | EsModuleComponent> | Component | void;
22+
) => Promise<Component | EsModuleComponent> | void;
23+
24+
/**
25+
* When the `Computed` type parameter on `ComponentOptions` is inferred,
26+
* it should have a property with the return type of every get-accessor.
27+
* Since there isn't a way to query for the return type of a function, we allow TypeScript
28+
* to infer from the shape of `Accessors<Computed>` and work backwards.
29+
*/
30+
export type Accessors<T> = {
31+
[K in keyof T]: (() => T[K]) | ComputedOptions<T[K]>
32+
}
1833

19-
export interface ComponentOptions<V extends Vue> {
20-
data?: Object | ((this: V) => Object);
21-
props?: string[] | { [key: string]: PropOptions | Constructor | Constructor[] };
34+
/**
35+
* This type should be used when an array of strings is used for a component's `props` value.
36+
*/
37+
export type ThisTypedComponentOptionsWithArrayProps<V extends Vue, Data, Methods, Computed, PropNames extends string> =
38+
object &
39+
ComponentOptions<V, Data | ((this: Readonly<Record<PropNames, any>> & V) => Data), Methods, Computed, PropNames[]> &
40+
ThisType<CombinedVueInstance<V, Data, Methods, Computed, Readonly<Record<PropNames, any>>>>;
41+
42+
/**
43+
* This type should be used when an object mapped to `PropOptions` is used for a component's `props` value.
44+
*/
45+
export type ThisTypedComponentOptionsWithRecordProps<V extends Vue, Data, Methods, Computed, Props> =
46+
object &
47+
ComponentOptions<V, Data | ((this: Readonly<Props> & V) => Data), Methods, Computed, RecordPropsDefinition<Props>> &
48+
ThisType<CombinedVueInstance<V, Data, Methods, Computed, Readonly<Props>>>;
49+
50+
type DefaultData<V> = object | ((this: V) => object);
51+
type DefaultProps = Record<string, any>;
52+
type DefaultMethods<V> = { [key: string]: (this: V, ...args: any[]) => any };
53+
type DefaultComputed = { [key: string]: any };
54+
export interface ComponentOptions<
55+
V extends Vue,
56+
Data=DefaultData<V>,
57+
Methods=DefaultMethods<V>,
58+
Computed=DefaultComputed,
59+
PropsDef=PropsDefinition<DefaultProps>> {
60+
data?: Data;
61+
props?: PropsDef;
2262
propsData?: Object;
23-
computed?: { [key: string]: ((this: V) => any) | ComputedOptions<V> };
24-
methods?: { [key: string]: (this: V, ...args: any[]) => any };
25-
watch?: { [key: string]: ({ handler: WatchHandler<V, any> } & WatchOptions) | WatchHandler<V, any> | string };
63+
computed?: Accessors<Computed>;
64+
methods?: Methods;
65+
watch?: Record<string, WatchOptionsWithHandler<any> | WatchHandler<any> | string>;
2666

2767
el?: Element | String;
2868
template?: string;
29-
render?(this: V, createElement: CreateElement): VNode;
69+
render?(createElement: CreateElement): VNode;
3070
renderError?: (h: () => VNode, err: Error) => VNode;
3171
staticRenderFns?: ((createElement: CreateElement) => VNode)[];
3272

3373
beforeCreate?(this: V): void;
34-
created?(this: V): void;
35-
beforeDestroy?(this: V): void;
36-
destroyed?(this: V): void;
37-
beforeMount?(this: V): void;
38-
mounted?(this: V): void;
39-
beforeUpdate?(this: V): void;
40-
updated?(this: V): void;
41-
activated?(this: V): void;
42-
deactivated?(this: V): void;
43-
44-
directives?: { [key: string]: DirectiveOptions | DirectiveFunction };
45-
components?: { [key: string]: Component | AsyncComponent };
74+
created?(): void;
75+
beforeDestroy?(): void;
76+
destroyed?(): void;
77+
beforeMount?(): void;
78+
mounted?(): void;
79+
beforeUpdate?(): void;
80+
updated?(): void;
81+
activated?(): void;
82+
deactivated?(): void;
83+
84+
directives?: { [key: string]: DirectiveFunction | DirectiveOptions };
85+
components?: { [key: string]: Component<any, any, any, any> | AsyncComponent<any, any, any, any> };
4686
transitions?: { [key: string]: Object };
4787
filters?: { [key: string]: Function };
4888

@@ -57,49 +97,64 @@ export interface ComponentOptions<V extends Vue> {
5797
parent?: Vue;
5898
mixins?: (ComponentOptions<Vue> | typeof Vue)[];
5999
name?: string;
100+
// TODO: support properly inferred 'extends'
60101
extends?: ComponentOptions<Vue> | typeof Vue;
61102
delimiters?: [string, string];
62103
comments?: boolean;
63104
inheritAttrs?: boolean;
64105
}
65106

66-
export interface FunctionalComponentOptions {
107+
export interface FunctionalComponentOptions<Props = DefaultProps, PropDefs = PropsDefinition<Props>> {
67108
name?: string;
68-
props?: string[] | { [key: string]: PropOptions | Constructor | Constructor[] };
109+
props?: PropDefs;
69110
inject?: { [key: string]: string | symbol } | string[];
70111
functional: boolean;
71-
render(this: never, createElement: CreateElement, context: RenderContext): VNode | void;
112+
render(this: undefined, createElement: CreateElement, context: RenderContext<Props>): VNode;
72113
}
73114

74-
export interface RenderContext {
75-
props: any;
115+
export interface RenderContext<Props=DefaultProps> {
116+
props: Props;
76117
children: VNode[];
77118
slots(): any;
78119
data: VNodeData;
79120
parent: Vue;
80121
injections: any
81122
}
82123

83-
export interface PropOptions {
84-
type?: Constructor | Constructor[] | null;
124+
export type Prop<T> = { (): T } | { new (...args: any[]): T & object }
125+
126+
export type PropValidator<T> = PropOptions<T> | Prop<T> | Prop<T>[];
127+
128+
export interface PropOptions<T=any> {
129+
type?: Prop<T> | Prop<T>[];
85130
required?: boolean;
86-
default?: any;
87-
validator?(value: any): boolean;
131+
default?: T | null | undefined | (() => object);
132+
validator?(value: T): boolean;
88133
}
89134

90-
export interface ComputedOptions<V> {
91-
get?(this: V): any;
92-
set?(this: V, value: any): void;
135+
export type RecordPropsDefinition<T> = {
136+
[K in keyof T]: PropValidator<T[K]>
137+
}
138+
export type ArrayPropsDefinition<T> = (keyof T)[];
139+
export type PropsDefinition<T> = ArrayPropsDefinition<T> | RecordPropsDefinition<T>;
140+
141+
export interface ComputedOptions<T> {
142+
get?(): T;
143+
set?(value: T): void;
93144
cache?: boolean;
94145
}
95146

96-
export type WatchHandler<V, T> = (this: V, val: T, oldVal: T) => void;
147+
export type WatchHandler<T> = (val: T, oldVal: T) => void;
97148

98149
export interface WatchOptions {
99150
deep?: boolean;
100151
immediate?: boolean;
101152
}
102153

154+
export interface WatchOptionsWithHandler<T> extends WatchOptions {
155+
handler: WatchHandler<T>;
156+
}
157+
103158
export type DirectiveFunction = (
104159
el: HTMLElement,
105160
binding: VNodeDirective,

types/test/augmentation-test.ts

+16-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import Vue = require("../index");
1+
import Vue from "../index";
22

33
declare module "../vue" {
44
// add instance property and method
@@ -8,9 +8,9 @@ declare module "../vue" {
88
}
99

1010
// add static property and method
11-
namespace Vue {
12-
const staticProperty: string;
13-
function staticMethod(): void;
11+
interface VueConstructor {
12+
staticProperty: string;
13+
staticMethod(): void;
1414
}
1515
}
1616

@@ -22,10 +22,21 @@ declare module "../options" {
2222
}
2323

2424
const vm = new Vue({
25+
props: ["bar"],
2526
data: {
2627
a: true
2728
},
28-
foo: "foo"
29+
foo: "foo",
30+
methods: {
31+
foo() {
32+
this.a = false;
33+
}
34+
},
35+
computed: {
36+
BAR(): string {
37+
return this.bar.toUpperCase();
38+
}
39+
}
2940
});
3041

3142
vm.$instanceProperty;

0 commit comments

Comments
 (0)