Sfoglia il codice sorgente

feat(types): further improve Vue type declarations for canonical usage (#6391)

Herrington Darkholme 8 anni fa
parent
commit
db138e2254

+ 2 - 2
package.json

@@ -79,7 +79,7 @@
     "eslint": "^3.0.0",
     "eslint-loader": "^1.7.1",
     "eslint-plugin-flowtype": "^2.34.0",
-    "eslint-plugin-jasmine": "^2.2.0",
+    "eslint-plugin-jasmine": "^2.8.4",
     "eslint-plugin-vue-libs": "^1.2.0",
     "file-loader": "^0.11.2",
     "flow-bin": "^0.54.0",
@@ -119,7 +119,7 @@
     "selenium-server": "^2.53.1",
     "serialize-javascript": "^1.3.0",
     "shelljs": "^0.7.8",
-    "typescript": "^2.3.4",
+    "typescript": "^2.5.2",
     "uglify-js": "^3.0.15",
     "webpack": "^2.6.1",
     "weex-js-runtime": "^0.22.0"

+ 31 - 32
types/index.d.ts

@@ -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";

+ 92 - 37
types/options.d.ts

@@ -1,48 +1,88 @@
-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;
+// we don't support infer props in async component
+export type Component<Data=DefaultData<Vue>, Methods=DefaultMethods<Vue>, Computed=DefaultComputed, Props=DefaultProps> =
+  | typeof Vue
+  | FunctionalComponentOptions<Props>
+  | ThisTypedComponentOptionsWithArrayProps<Vue, Data, Methods, Computed, keyof Props>
+  | ThisTypedComponentOptionsWithRecordProps<Vue, Data, Methods, Computed, Props>;
 
 interface EsModuleComponent {
   default: Component
 }
 
-export type AsyncComponent = (
-  resolve: (component: Component) => void,
+export type AsyncComponent<Data=DefaultData<Vue>, Methods=DefaultMethods<Vue>, Computed=DefaultComputed, Props=DefaultProps> = (
+  resolve: (component: Component<Data, Methods, Computed, Props>) => void,
   reject: (reason?: any) => void
-) => Promise<Component | EsModuleComponent> | Component | void;
+) => Promise<Component | EsModuleComponent> | 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<V extends Vue, Data, Methods, Computed, PropNames extends string> =
+  object &
+  ComponentOptions<V, Data | ((this: Readonly<Record<PropNames, any>> & V) => Data), Methods, Computed, PropNames[]> &
+  ThisType<CombinedVueInstance<V, Data, Methods, Computed, Readonly<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<V extends Vue, Data, Methods, Computed, Props> =
+  object &
+  ComponentOptions<V, Data | ((this: Readonly<Props> & V) => Data), Methods, Computed, RecordPropsDefinition<Props>> &
+  ThisType<CombinedVueInstance<V, Data, Methods, Computed, Readonly<Props>>>;
+
+type DefaultData<V> =  object | ((this: V) => object);
+type DefaultProps = Record<string, any>;
+type DefaultMethods<V> =  { [key: string]: (this: V, ...args: any[]) => any };
+type DefaultComputed = { [key: string]: any };
+export interface ComponentOptions<
+  V extends Vue,
+  Data=DefaultData<V>,
+  Methods=DefaultMethods<V>,
+  Computed=DefaultComputed,
+  PropsDef=PropsDefinition<DefaultProps>> {
+  data?: Data;
+  props?: PropsDef;
   propsData?: Object;
-  computed?: { [key: string]: ((this: V) => any) | ComputedOptions<V> };
-  methods?: { [key: string]: (this: V, ...args: any[]) => any };
-  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 };
+  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, any> | AsyncComponent<any, any, any, any> };
   transitions?: { [key: string]: Object };
   filters?: { [key: string]: Function };
 
@@ -57,22 +97,23 @@ export interface ComponentOptions<V extends Vue> {
   parent?: Vue;
   mixins?: (ComponentOptions<Vue> | typeof Vue)[];
   name?: string;
+  // TODO: support properly inferred 'extends'
   extends?: ComponentOptions<Vue> | typeof Vue;
   delimiters?: [string, string];
   comments?: boolean;
   inheritAttrs?: boolean;
 }
 
-export interface FunctionalComponentOptions {
+export interface FunctionalComponentOptions<Props = DefaultProps, PropDefs = PropsDefinition<Props>> {
   name?: string;
-  props?: string[] | { [key: string]: PropOptions | Constructor | Constructor[] };
+  props?: PropDefs;
   inject?: { [key: string]: string | symbol } | string[];
   functional: boolean;
-  render(this: never, createElement: CreateElement, context: RenderContext): VNode | void;
+  render(this: undefined, createElement: CreateElement, context: RenderContext<Props>): VNode;
 }
 
-export interface RenderContext {
-  props: any;
+export interface RenderContext<Props=DefaultProps> {
+  props: Props;
   children: VNode[];
   slots(): any;
   data: VNodeData;
@@ -80,26 +121,40 @@ export interface RenderContext {
   injections: any
 }
 
-export interface PropOptions {
-  type?: Constructor | Constructor[] | null;
+export type Prop<T> = { (): T } | { new (...args: any[]): T & object }
+
+export type PropValidator<T> = PropOptions<T> | Prop<T> | Prop<T>[];
+
+export interface PropOptions<T=any> {
+  type?: Prop<T> | Prop<T>[];
   required?: boolean;
-  default?: any;
-  validator?(value: any): boolean;
+  default?: T | null | undefined | (() => object);
+  validator?(value: T): boolean;
 }
 
-export interface ComputedOptions<V> {
-  get?(this: V): any;
-  set?(this: V, value: any): void;
+export type RecordPropsDefinition<T> = {
+  [K in keyof T]: PropValidator<T[K]>
+}
+export type ArrayPropsDefinition<T> = (keyof T)[];
+export type PropsDefinition<T> = ArrayPropsDefinition<T> | RecordPropsDefinition<T>;
+
+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,

+ 16 - 5
types/test/augmentation-test.ts

@@ -1,4 +1,4 @@
-import Vue = require("../index");
+import Vue from "../index";
 
 declare module "../vue" {
   // add instance property and method
@@ -8,9 +8,9 @@ declare module "../vue" {
   }
 
   // add static property and method
-  namespace Vue {
-    const staticProperty: string;
-    function staticMethod(): void;
+  interface VueConstructor {
+    staticProperty: string;
+    staticMethod(): void;
   }
 }
 
@@ -22,10 +22,21 @@ declare module "../options" {
 }
 
 const vm = new Vue({
+  props: ["bar"],
   data: {
     a: true
   },
-  foo: "foo"
+  foo: "foo",
+  methods: {
+    foo() {
+      this.a = false;
+    }
+  },
+  computed: {
+    BAR(): string {
+      return this.bar.toUpperCase();
+    }
+  }
 });
 
 vm.$instanceProperty;

+ 89 - 24
types/test/options-test.ts

@@ -1,14 +1,76 @@
-import Vue = require("../index");
+import Vue from "../index";
 import { AsyncComponent, ComponentOptions, FunctionalComponentOptions } from "../index";
+import { CreateElement } from "../vue";
 
 interface Component extends Vue {
   a: number;
 }
 
+Vue.component('sub-component', {
+  components: {
+    a: Vue.component(""),
+    b: {}
+  }
+});
+
+Vue.component('prop-component', {
+  props: {
+    size: Number,
+    name: {
+      type: String,
+      default: '0',
+      required: true,
+    }
+  },
+  data() {
+    return {
+      fixedSize: this.size.toFixed(),
+      capName: this.name.toUpperCase()
+    }
+  }
+});
+
+Vue.component('string-prop', {
+  props: ['size', 'name'],
+  data() {
+    return {
+      fixedSize: this.size.whatever,
+      capName: this.name.isany
+    }
+  }
+});
+
+class User {
+  private u: number
+}
+class Cat {
+  private u: number
+}
+
+Vue.component('union-prop', {
+  props: {
+    primitive: [String, Number],
+    object: [Cat, User],
+    regex: RegExp,
+    mixed: [RegExp, Array],
+    union: [User, Number] as {new(): User | Number}[] // requires annotation
+  },
+  data() {
+    this.primitive;
+    this.object;
+    this.union;
+    this.regex.compile;
+    this.mixed;
+    return {
+      fixedSize: this.union,
+    }
+  }
+});
+
 Vue.component('component', {
   data() {
     this.$mount
-    this.a
+    this.size
     return {
       a: 1
     }
@@ -17,25 +79,22 @@ Vue.component('component', {
     size: Number,
     name: {
       type: String,
-      default: 0,
+      default: '0',
       required: true,
-      validator(value) {
-        return value > 0;
-      }
     }
   },
   propsData: {
     msg: "Hello"
   },
   computed: {
-    aDouble(this: Component) {
+    aDouble(): number {
       return this.a * 2;
     },
     aPlus: {
-      get(this: Component) {
+      get(): number {
         return this.a + 1;
       },
-      set(this: Component, v: number) {
+      set(v: number) {
         this.a = v - 1;
       },
       cache: false
@@ -44,6 +103,9 @@ Vue.component('component', {
   methods: {
     plus() {
       this.a++;
+      this.aDouble.toFixed();
+      this.aPlus = 1;
+      this.size.toFixed();
     }
   },
   watch: {
@@ -92,15 +154,15 @@ Vue.component('component', {
       createElement("div", "message"),
       createElement(Vue.component("component")),
       createElement({} as ComponentOptions<Vue>),
-      createElement({ functional: true, render () {}}),
+      createElement({
+        functional: true,
+        render(c: CreateElement) {
+          return createElement()
+        }
+      }),
 
       createElement(() => Vue.component("component")),
       createElement(() => ( {} as ComponentOptions<Vue> )),
-      createElement(() => {
-        return new Promise((resolve) => {
-          resolve({} as ComponentOptions<Vue>);
-        })
-      }),
       createElement((resolve, reject) => {
         resolve({} as ComponentOptions<Vue>);
         reject();
@@ -114,7 +176,7 @@ Vue.component('component', {
   staticRenderFns: [],
 
   beforeCreate() {
-    this.a = 1;
+    (this as any).a = 1;
   },
   created() {},
   beforeDestroy() {},
@@ -160,7 +222,7 @@ Vue.component('component', {
   name: "Component",
   extends: {} as ComponentOptions<Vue>,
   delimiters: ["${", "}"]
-} as ComponentOptions<Component>);
+});
 
 Vue.component('component-with-scoped-slot', {
   render (h) {
@@ -183,15 +245,15 @@ Vue.component('component-with-scoped-slot', {
   },
   components: {
     child: {
-      render (h) {
+      render (this: Vue, h: CreateElement) {
         return h('div', [
           this.$scopedSlots['default']({ msg: 'hi' }),
           this.$scopedSlots['item']({ msg: 'hello' })
         ])
       }
-    } as ComponentOptions<Vue>
+    }
   }
-} as ComponentOptions<Vue>)
+})
 
 Vue.component('functional-component', {
   props: ['prop'],
@@ -205,13 +267,16 @@ Vue.component('functional-component', {
     context.parent;
     return createElement("div", {}, context.children);
   }
-} as FunctionalComponentOptions);
+});
 
 Vue.component('functional-component-object-inject', {
   functional: true,
   inject: {
     foo: 'bar',
     baz: Symbol()
+  },
+  render(h) {
+    return h('div')
   }
 })
 
@@ -220,8 +285,8 @@ Vue.component("async-component", ((resolve, reject) => {
     resolve(Vue.component("component"));
   }, 0);
   return new Promise((resolve) => {
-    resolve({ functional: true } as FunctionalComponentOptions);
+    resolve({ functional: true });
   })
-}) as AsyncComponent);
+}));
 
-Vue.component('async-es-module-component', (() => import('./es-module')) as AsyncComponent)
+Vue.component('async-es-module-component', () => import('./es-module'))

+ 1 - 1
types/test/plugin-test.ts

@@ -1,4 +1,4 @@
-import Vue = require("../index");
+import Vue from "../index";
 import { PluginFunction, PluginObject } from "../index";
 
 class Option {

+ 1 - 2
types/test/tsconfig.json

@@ -6,8 +6,7 @@
       "es2015"
     ],
     "module": "commonjs",
-    "noImplicitAny": true,
-    "strictNullChecks": true,
+    "strict": true,
     "noEmit": true
   },
   "files": [

+ 85 - 2
types/test/vue-test.ts

@@ -1,4 +1,5 @@
-import Vue = require("../index");
+import Vue, { VNode } from "../index";
+import { ComponentOptions } from "../options";
 
 class Test extends Vue {
   a: number;
@@ -90,9 +91,91 @@ class Test extends Vue {
     this.directive("", {bind() {}});
     this.filter("", (value: number) => value);
     this.component("", { data: () => ({}) });
-    this.component("", { functional: true, render () {}});
+    this.component("", { functional: true, render(h) { return h("div", "hello!") } });
     this.use;
     this.mixin(Test);
     this.compile("<div>{{ message }}</div>");
   }
 }
+
+const HelloWorldComponent = Vue.extend({
+  props: ["name"],
+  data() {
+    return {
+      message: "Hello " + this.name,
+    }
+  },
+  computed: {
+    shouted(): string {
+      return this.message.toUpperCase();
+    }
+  },
+  methods: {
+    getMoreExcited() {
+      this.message += "!";
+    }
+  },
+  watch: {
+    message(a: string) {
+      console.log(`Message ${this.message} was changed!`);
+    }
+  }
+});
+
+const FunctionalHelloWorldComponent = Vue.extend({
+  functional: true,
+  props: ["name"],
+  render(createElement, ctxt) {
+    return createElement("div", "Hello " + ctxt.props.name)
+  }
+});
+
+const Parent = Vue.extend({
+  data() {
+    return { greeting: 'Hello' }
+  }
+});
+
+const Child = Parent.extend({
+  methods: {
+    foo() {
+      console.log(this.greeting.toLowerCase());
+    }
+  }
+});
+
+const GrandChild = Child.extend({
+  computed: {
+    lower(): string {
+      return this.greeting.toLowerCase();
+    }
+  }
+});
+
+new GrandChild().lower.toUpperCase();
+for (let _ in (new Test()).$options) {
+}
+declare const options: ComponentOptions<Vue>;
+Vue.extend(options);
+Vue.component('test-comp', options);
+new Vue(options);
+
+// cyclic example
+Vue.extend({
+  props: {
+    bar: {
+      type: String
+    }
+  },
+  methods: {
+    foo() {}
+  },
+  mounted () {
+    this.foo()
+  },
+  // manual annotation
+  render (h): VNode {
+    const a = this.bar
+    return h('canvas', {}, [a])
+  }
+})

+ 11 - 0
types/tsconfig.json

@@ -0,0 +1,11 @@
+{
+    "compilerOptions": {
+        "strict": true,
+        "lib": [
+            "es2015", "dom"
+        ]
+    },
+    "include": [
+        "./*.ts"
+    ]
+}

+ 66 - 52
types/vue.d.ts

@@ -3,50 +3,39 @@ import {
   AsyncComponent,
   ComponentOptions,
   FunctionalComponentOptions,
-  WatchOptions,
+  WatchOptionsWithHandler,
   WatchHandler,
   DirectiveOptions,
-  DirectiveFunction
+  DirectiveFunction,
+  RecordPropsDefinition,
+  ThisTypedComponentOptionsWithArrayProps,
+  ThisTypedComponentOptionsWithRecordProps,
+  WatchOptions,
 } from "./options";
 import { VNode, VNodeData, VNodeChildren, ScopedSlot } from "./vnode";
 import { PluginFunction, PluginObject } from "./plugin";
 
-export type CreateElement = {
-  // empty node
-  (): VNode;
-
-  // element or component name
-  (tag: string, children: VNodeChildren): VNode;
-  (tag: string, data?: VNodeData, children?: VNodeChildren): VNode;
-
-  // component constructor or options
-  (tag: Component, children: VNodeChildren): VNode;
-  (tag: Component, data?: VNodeData, children?: VNodeChildren): VNode;
-
-  // async component
-  (tag: AsyncComponent, children: VNodeChildren): VNode;
-  (tag: AsyncComponent, data?: VNodeData, children?: VNodeChildren): VNode;
+export interface CreateElement {
+  (tag?: string | Component<any, any, any, any> | AsyncComponent<any, any, any, any>, children?: VNodeChildren): VNode;
+  (tag?: string | Component<any, any, any, any> | AsyncComponent<any, any, any, any>, data?: VNodeData, children?: VNodeChildren): VNode;
 }
 
-export declare class Vue {
-
-  constructor(options?: ComponentOptions<Vue>);
-
-  $data: Object;
+export interface Vue {
   readonly $el: HTMLElement;
   readonly $options: ComponentOptions<this>;
   readonly $parent: Vue;
   readonly $root: Vue;
   readonly $children: Vue[];
-  readonly $refs: { [key: string]: Vue | Element | Vue[] | Element[]};
+  readonly $refs: { [key: string]: Vue | Element | Vue[] | Element[] };
   readonly $slots: { [key: string]: VNode[] };
   readonly $scopedSlots: { [key: string]: ScopedSlot };
   readonly $isServer: boolean;
+  readonly $data: Record<string, any>;
+  readonly $props: Record<string, any>;
   readonly $ssrContext: any;
-  readonly $props: any;
   readonly $vnode: VNode;
-  readonly $attrs: { [key: string] : string };
-  readonly $listeners: { [key: string]: Function | Array<Function> };
+  readonly $attrs: Record<string, string>;
+  readonly $listeners: Record<string, Function | Function[]>;
 
   $mount(elementOrSelector?: Element | String, hydrating?: boolean): this;
   $forceUpdate(): void;
@@ -55,12 +44,12 @@ export declare class Vue {
   $delete: typeof Vue.delete;
   $watch(
     expOrFn: string,
-    callback: WatchHandler<this, any>,
+    callback: (this: this, n: any, o: any) => void,
     options?: WatchOptions
   ): (() => void);
   $watch<T>(
     expOrFn: (this: this) => T,
-    callback: WatchHandler<this, T>,
+    callback: (this: this, n: T, o: T) => void,
     options?: WatchOptions
   ): (() => void);
   $on(event: string | string[], callback: Function): this;
@@ -70,8 +59,54 @@ export declare class Vue {
   $nextTick(callback: (this: this) => void): void;
   $nextTick(): Promise<void>;
   $createElement: CreateElement;
+}
+
+export type CombinedVueInstance<Instance extends Vue, Data, Methods, Computed, Props> = Instance & Data & Methods & Computed & Props;
+export type ExtendedVue<Instance extends Vue, Data, Methods, Computed, Props> = VueConstructor<CombinedVueInstance<Instance, Data, Methods, Computed, Props> & Vue>;
+
+export interface VueConstructor<V extends Vue = Vue> {
+  new <Data = object, Methods = object, Computed = object, PropNames extends string = never>(options?: ThisTypedComponentOptionsWithArrayProps<V, Data, Methods, Computed, PropNames>): CombinedVueInstance<V, Data, Methods, Computed, Record<PropNames, any>>;
+  // ideally, the return type should just contains Props, not Record<keyof Props, any>. But TS requires Base constructors must all have the same return type.
+  new <Data = object, Methods = object, Computed = object, Props = object>(options?: ThisTypedComponentOptionsWithRecordProps<V, Data, Methods, Computed, Props>): CombinedVueInstance<V, Data, Methods, Computed, Record<keyof Props, any>>;
+  new (options?: ComponentOptions<V>): CombinedVueInstance<V, object, object, object, Record<keyof object, any>>;
+
+  extend<PropNames extends string = never>(definition: FunctionalComponentOptions<Record<PropNames, any>, PropNames[]>): ExtendedVue<V, {}, {}, {}, Record<PropNames, any>>;
+  extend<Props>(definition: FunctionalComponentOptions<Props, RecordPropsDefinition<Props>>): ExtendedVue<V, {}, {}, {}, Props>;
+  extend<Data, Methods, Computed, PropNames extends string>(options?: ThisTypedComponentOptionsWithArrayProps<V, Data, Methods, Computed, PropNames>): ExtendedVue<V, Data, Methods, Computed, Record<PropNames, any>>;
+  extend<Data, Methods, Computed, Props>(options?: ThisTypedComponentOptionsWithRecordProps<V, Data, Methods, Computed, Props>): ExtendedVue<V, Data, Methods, Computed, Props>;
+  extend(options?: ComponentOptions<V>): ExtendedVue<V, {}, {}, {}, {}>;
+
+  nextTick(callback: () => void, context?: any[]): void;
+  nextTick(): Promise<void>
+  set<T>(object: Object, key: string, value: T): T;
+  set<T>(array: T[], key: number, value: T): T;
+  delete(object: Object, key: string): void;
+  delete<T>(array: T[], key: number): void;
+
+  directive(
+    id: string,
+    definition?: DirectiveOptions | DirectiveFunction
+  ): DirectiveOptions;
+  filter(id: string, definition?: Function): Function;
 
-  static config: {
+  component(id: string): VueConstructor;
+  component<VC extends VueConstructor>(id: string, constructor: VC): VC;
+  component<Data, Methods, Computed, Props>(id: string, definition: AsyncComponent<Data, Methods, Computed, Props>): ExtendedVue<V, Data, Methods, Computed, Props>;
+  component<PropNames extends string>(id: string, definition: FunctionalComponentOptions<Record<PropNames, any>, PropNames[]>): ExtendedVue<V, {}, {}, {}, Record<PropNames, any>>;
+  component<Props>(id: string, definition: FunctionalComponentOptions<Props, RecordPropsDefinition<Props>>): ExtendedVue<V, {}, {}, {}, Props>;
+  component<Data, Methods, Computed, PropNames extends string>(id: string, definition?: ThisTypedComponentOptionsWithArrayProps<V, Data, Methods, Computed, PropNames>): ExtendedVue<V, Data, Methods, Computed, Record<PropNames, any>>;
+  component<Data, Methods, Computed, Props>(id: string, definition?: ThisTypedComponentOptionsWithRecordProps<V, Data, Methods, Computed, Props>): ExtendedVue<V, Data, Methods, Computed, Props>;
+  component(id: string, definition?: ComponentOptions<V>): ExtendedVue<V, {}, {}, {}, {}>;
+
+  use<T>(plugin: PluginObject<T> | PluginFunction<T>, options?: T): void;
+  use(plugin: PluginObject<any> | PluginFunction<any>, ...options: any[]): void;
+  mixin(mixin: VueConstructor | ComponentOptions<Vue>): void;
+  compile(template: string): {
+    render(createElement: typeof Vue.prototype.$createElement): VNode;
+    staticRenderFns: (() => VNode)[];
+  };
+
+  config: {
     silent: boolean;
     optionMergeStrategies: any;
     devtools: boolean;
@@ -82,27 +117,6 @@ export declare class Vue {
     ignoredElements: string[];
     keyCodes: { [key: string]: number | number[] };
   }
-
-  static extend(options: ComponentOptions<Vue> | FunctionalComponentOptions): typeof Vue;
-  static nextTick(callback: () => void, context?: any[]): void;
-  static nextTick(): Promise<void>
-  static set<T>(object: Object, key: string, value: T): T;
-  static set<T>(array: T[], key: number, value: T): T;
-  static delete(object: Object, key: string): void;
-  static delete<T>(array: T[], key: number): void;
-
-  static directive(
-    id: string,
-    definition?: DirectiveOptions | DirectiveFunction
-  ): DirectiveOptions;
-  static filter(id: string, definition?: Function): Function;
-  static component(id: string, definition?: Component | AsyncComponent): typeof Vue;
-
-  static use<T>(plugin: PluginObject<T> | PluginFunction<T>, options?: T): void;
-  static use(plugin: PluginObject<any> | PluginFunction<any>, ...options: any[]): void;
-  static mixin(mixin: typeof Vue | ComponentOptions<Vue>): void;
-  static compile(template: string): {
-    render(createElement: typeof Vue.prototype.$createElement): VNode;
-    staticRenderFns: (() => VNode)[];
-  };
 }
+
+export const Vue: VueConstructor;