Repository: vuejs/vue
Pull request: Update ComponentOptions

- Make ComponentOptions as a Generic Interface
- Update some interfaces and types related to ComponentOptions
- Update tests
1. When defining `ComponentOptions.data` as Function, `this` is specified, but when it defined as union types, `this` isn't specified. So the former one is better, I think.
2. These changes enable us to do something like below. Adapting decorators to the `ComponentOptions<V>`, we don't have to define some methods in a class statement that is not a instance method.

``` typescript

@VueComponent<MyComponent>({
  data () {
    return {
      message: this.method()
    }
  },
  beforeCreate () {
    this.message = ""
  },
  render (h) {
    return h("div", {}, this.message)
  }
})
class MyComponent extends Vue {
  message: string;

  method(): string {
    return "";
  }
}
```


Changed files:
filename: types/index.d.ts
patch: @@ -6,12 +6,12 @@ import * as VNode from "./vnode";
 // `Vue` in `export = Vue` must be a namespace
 // All available types are exported via this namespace
 declare namespace Vue {
-  export type ComponentOptions = Options.ComponentOptions;
+  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 = Options.ComputedOptions;
-  export type WatchHandler = Options.WatchHandler;
+  export type ComputedOptions<V extends Vue> = Options.ComputedOptions<V>;
+  export type WatchHandler<V extends Vue> = Options.WatchHandler<V>;
   export type WatchOptions = Options.WatchOptions;
   export type DirectiveFunction = Options.DirectiveFunction;
   export type DirectiveOptions = Options.DirectiveOptions;

filename: types/options.d.ts
patch: @@ -7,37 +7,37 @@ type Constructor = {
 
 type $createElement = typeof Vue.prototype.$createElement;
 
-export interface ComponentOptions {
-  data?: Object | ( (this: Vue) => Object );
+export interface ComponentOptions<V extends Vue> {
+  data?: Object | ((this: V) => Object);
   props?: string[] | { [key: string]: PropOptions | Constructor | Constructor[] };
   propsData?: Object;
-  computed?: { [key: string]: ((this: Vue) => any) | ComputedOptions };
+  computed?: { [key: string]: ((this: V) => any) | ComputedOptions<V> };
   methods?: { [key: string]: Function };
-  watch?: { [key: string]: ({ handler: WatchHandler } & WatchOptions) | WatchHandler | string };
+  watch?: { [key: string]: ({ handler: WatchHandler<V> } & WatchOptions) | WatchHandler<V> | string };
 
   el?: Element | String;
   template?: string;
-  render?(createElement: $createElement): VNode;
-  staticRenderFns?: (() => VNode)[];
+  render?(this: V, createElement: $createElement): VNode;
+  staticRenderFns?: ((createElement: $createElement) => VNode)[];
 
-  beforeCreate?(): void;
-  created?(): void;
-  beforeDestroy?(): void;
-  destroyed?(): void;
-  beforeMount?(): void;
-  mounted?(): void;
-  beforeUpdate?(): void;
-  updated?(): void;
+  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;
 
   directives?: { [key: string]: DirectiveOptions | DirectiveFunction };
-  components?: { [key: string]: ComponentOptions | FunctionalComponentOptions | typeof Vue };
+  components?: { [key: string]: ComponentOptions<Vue> | FunctionalComponentOptions | typeof Vue };
   transitions?: { [key: string]: Object };
   filters?: { [key: string]: Function };
 
   parent?: Vue;
-  mixins?: (ComponentOptions | typeof Vue)[];
+  mixins?: (ComponentOptions<Vue> | typeof Vue)[];
   name?: string;
-  extends?: ComponentOptions | typeof Vue;
+  extends?: ComponentOptions<Vue> | typeof Vue;
   delimiters?: [string, string];
 }
 
@@ -63,13 +63,13 @@ export interface PropOptions {
   validator?(value: any): boolean;
 }
 
-export interface ComputedOptions {
-  get?(this: Vue): any;
-  set?(this: Vue, value: any): void;
+export interface ComputedOptions<V> {
+  get?(this: V): any;
+  set?(this: V, value: any): void;
   cache?: boolean;
 }
 
-export type WatchHandler = <T>(val: T, oldVal: T) => void;
+export type WatchHandler<V> = (this: V, val: any, oldVal: any) => void;
 
 export interface WatchOptions {
   deep?: boolean;

filename: types/test/options-test.ts
patch: @@ -7,6 +7,8 @@ interface Component extends Vue {
 
 Vue.component('component', {
   data() {
+    this.$mount
+    this.a
     return {
       a: 1
     }
@@ -50,7 +52,9 @@ Vue.component('component', {
     },
     'b': 'someMethod',
     'c': {
-      handler(val: number, oldval: number) {},
+      handler(val, oldVal) {
+        this.a = val
+      },
       deep: true
     }
   },
@@ -91,7 +95,9 @@ Vue.component('component', {
   },
   staticRenderFns: [],
 
-  beforeCreate() {},
+  beforeCreate() {
+    this.a = 1;
+  },
   created() {},
   beforeDestroy() {},
   destroyed() {},
@@ -120,7 +126,7 @@ Vue.component('component', {
   },
   components: {
     a: Vue.component(""),
-    b: {} as ComponentOptions
+    b: {} as ComponentOptions<Vue>
   },
   transitions: {},
   filters: {
@@ -129,11 +135,11 @@ Vue.component('component', {
     }
   },
   parent: new Vue,
-  mixins: [Vue.component(""), ({} as ComponentOptions)],
+  mixins: [Vue.component(""), ({} as ComponentOptions<Vue>)],
   name: "Component",
-  extends: {} as ComponentOptions,
+  extends: {} as ComponentOptions<Vue>,
   delimiters: ["${", "}"]
-} as ComponentOptions);
+} as ComponentOptions<Component>);
 
 Vue.component('functional-component', {
   props: ['prop'],

filename: types/vue.d.ts
patch: @@ -11,11 +11,11 @@ import { PluginFunction, PluginObject } from "./plugin";
 
 export declare class Vue {
 
-  constructor(options?: ComponentOptions);
+  constructor(options?: ComponentOptions<Vue>);
 
   $data: Object;
   readonly $el: HTMLElement;
-  readonly $options: ComponentOptions;
+  readonly $options: ComponentOptions<this>;
   readonly $parent: Vue;
   readonly $root: Vue;
   readonly $children: Vue[];
@@ -30,7 +30,7 @@ export declare class Vue {
   $delete: typeof Vue.delete;
   $watch(
     expOrFn: string | Function,
-    callback: WatchHandler,
+    callback: WatchHandler<this>,
     options?: WatchOptions
   ): (() => void);
   $on(event: string, callback: Function): this;
@@ -54,7 +54,7 @@ export declare class Vue {
     keyCodes: { [key: string]: number };
   }
 
-  static extend(options: ComponentOptions): typeof Vue;
+  static extend(options: ComponentOptions<Vue>): typeof Vue;
   static nextTick(callback: () => void, context?: any[]): void;
   static set<T>(object: Object, key: string, value: T): T;
   static set<T>(array: T[], key: number, value: T): T;
@@ -67,11 +67,11 @@ export declare class Vue {
   static filter(id: string, definition?: Function): Function;
   static component(
     id: string,
-    definition?: ComponentOptions | FunctionalComponentOptions | typeof Vue
+    definition?: ComponentOptions<Vue> | FunctionalComponentOptions | typeof Vue
   ): typeof Vue;
 
   static use<T>(plugin: PluginObject<T> | PluginFunction<T>, options?: T): void;
-  static mixin(mixin: typeof Vue | ComponentOptions): void;
+  static mixin(mixin: typeof Vue | ComponentOptions<Vue>): void;
   static compile(template: string): {
     render(createElement: typeof Vue.prototype.$createElement): VNode;
     staticRenderFns: (() => VNode)[];