Skip to content

Add typescript definition #3509

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

Closed
wants to merge 11 commits into from
Closed
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions types/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import {Vue} from "./vue.d";
export = Vue;
72 changes: 72 additions & 0 deletions types/options.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { Vue } from "./vue.d";
import { VNode, VNodeDirective } from "./vnode.d";

type Constructor = {
new (...args: any[]): any;
}

export interface ComponentOptions {
data?: Object | ( (this: Vue) => Object );
props?: string[] | { [key: string]: PropOptions | Constructor | Constructor[] };
propsData?: Object;
computed?: { [key: string]: ((this: Vue) => any) | ComputedOptions };
methods?: { [key: string]: Function };
watch?: { [key: string]: ({ handler: WatchHandler } & WatchOptions) | WatchHandler | string };

el?: Element | String;
template?: string;
render?(createElement: typeof Vue.prototype.$createElement): VNode;
Copy link
Contributor Author

@kaorun343 kaorun343 Sep 2, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

interface ComponentOptions {
  functional?: boolean;
  render?(createElement: typeof Vue.prototype.$createElement: context: ContextObject): VNode;
}
interface ContextObject {
  readonly props: any;
  readonly children: VNode[];
  readonly slots: any;
  readonly data: any;
  readonly parent: VNode;
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a union type is suitable for ComponentOptions? One for functional component and another for normal components.

Also render can be even more strict if it is typed like render(this: never, createElement: typeof Vue.prototype.$createElement, content: ContextObject). So users cannot access this in functional components.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's nice to make like FunctionalComponentOptions. This interface will tell us what properties are available / disable for functional components (Probably, lifecycle hooks and data are not available).

staticRenderFns?: (() => VNode)[];

init?(): void;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems init is changed to beforeCreated. #2873

created?(): void;
beforeMount?(): void;
mounted?(): void;
beforeUpdate?(): void;
updated?(): void;

directives?: { [key: string]: DirectiveOptions | DirectiveFunction };
components?: { [key: string]: ComponentOptions | typeof Vue };
transitions?: { [key: string]: Object };
filters?: { [key: string]: Function };

parent?: Vue;
mixins?: (ComponentOptions | typeof Vue)[];
name?: string;
extends?: ComponentOptions | typeof Vue;
delimiters?: [string, string];
}

export interface PropOptions {
type?: Constructor | Constructor[] | null;
required?: boolean;
default?: any;
validator?(value: any): boolean;
}

export interface ComputedOptions {
get?(this: Vue): any;
set?(this: Vue, value: any): void;
cache?: boolean;
}

export type WatchHandler = <T>(val: T, oldVal: T) => void;

export interface WatchOptions {
deep?: boolean;
immediate?: boolean;
}

export type DirectiveFunction = (
el: HTMLElement,
binding: VNodeDirective,
vnode: VNode,
oldVnode: VNode
) => void;

export interface DirectiveOptions {
bind?: DirectiveFunction;
update?: DirectiveFunction;
componentUpdated?: DirectiveFunction;
unbind?: DirectiveFunction;
}
10 changes: 10 additions & 0 deletions types/plugin.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Vue as _Vue } from "./vue.d";

export interface PluginFunction<T> {
(Vue: typeof _Vue, options?: T): void;
}

export interface PluginObject<T> {
install: PluginFunction<T>;
[key: string]: any;
}
97 changes: 97 additions & 0 deletions types/test/options-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { Vue } from "../vue.d";
import { ComponentOptions } from "../options.d";

interface Component extends Vue {
a: number;
}

const Options: ComponentOptions = {
data() {
return {
a: 1
}
},
props: {
size: Number,
name: {
type: String,
default: 0,
required: true,
validator(value) {
return value > 0;
}
}
},
propsData: {
msg: "Hello"
},
computed: {
aDouble(this: Component) {
return this.a * 2;
},
aPlus: {
get(this: Component) {
return this.a + 1;
},
set(this: Component, v: number) {
this.a = v - 1;
},
cache: false
}
},
methods: {
plus(this: Component) {
this.a++;
}
},
watch: {
'a': function(val: number, oldVal: number) {
console.log(`new: ${val}, old: ${oldVal}`);
},
'b': 'someMethod',
'c': {
handler(val: number, oldval: number) {},
deep: true
}
},
el: "#app",
template: "<div>{{ message }}</div>",
render(createElement) {
return createElement("div", {}, this.message);
},
staticRenderFns: [],

init() {},
created() {},
beforeMount() {},
mounted() {},
beforeUpdate() {},
updated() {},

directives: {
a: {
bind() {},
update() {},
componentMounted() {},
unbind() {}
},
b(el, binding, vnode, oldVnode) {
el.textContent;
}
},
components: {
a: Vue.component(""),
b: {} as ComponentOptions
},
transitions: {},
filters: {
double(value: number) {
return value * 2;
}
},
parent: new Vue,
mixins: [Vue.component(""), ({} as ComponentOptions)],
name: "Component",
extends: {} as ComponentOptions,
delimiters: ["${", "}"]
}
19 changes: 19 additions & 0 deletions types/test/plugin-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Vue } from "../vue.d";
import { PluginFunction, PluginObject } from "../plugin.d";

class Option {
prefix: string;
suffix: string;
}

const plugin: PluginObject<Option> = {
install(Vue, option) {
if (typeof option !== "undefined") {
const {prefix, suffix} = option;
}
}
}
const installer: PluginFunction<Option> = function(Vue, option) { }

Vue.use(plugin, new Option);
Vue.use(installer, new Option);
19 changes: 19 additions & 0 deletions types/test/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"noImplicitAny": true,
"strictNullChecks": true
},
"files": [
"../index.d.ts",
"../options.d.ts",
"../plugin.d.ts",
"../vnode.d.ts",
"../vue.d.ts",
"options-test.ts",
"plugin-test.ts",
"vue-test.ts"
],
"compileOnSave": false
}
70 changes: 70 additions & 0 deletions types/test/vue-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { Vue } from "../vue.d";

class Test extends Vue {
testProperties() {
this.$data;
this.$el;
this.$options;
this.$parent;
this.$root;
this.$children;
this.$refs;
this.$slots;
this.$isServer;
}

testMethods() {
this.$mount("#app", false);
this.$forceUpdate();
this.$destroy();
this.$set({}, "key", "value");
this.$delete({}, "key");
this.$watch("a", (val: number, oldVal: number) => {}, {
immediate: true,
deep: false
})();
this.$watch(() => {}, (val: number) => {});
this.$on("", () => {});
this.$once("", () => {});
this.$off("", () => {});
this.$emit("", 1, 2, 3);
this.$nextTick(function() {
this.$nextTick;
});
this.$createElement("div", {}, "message", "");
}

static testConfig() {
const { config } = this;
config.silent;
config.optionMergeStrategies;
config.devtools;
config.errorHandler = (err, vm) => {
if (vm instanceof Test) {
vm.testProperties();
vm.testMethods();
}
};
config.keyCodes = { esc: 27 };
}

static testMethods() {
this.extend({
data() {
return {
msg: ""
};
}
});
this.nextTick(() => {});
this.set({}, "", "");
this.set([true, false, true], 1, true);
this.delete({}, "");
this.directive("", {bind() {}});
this.filter("", (value: number) => value);
this.component("", { data: () => ({}) });
this.use;
this.mixin(Test);
this.compile("<div>{{ message }}</div>");
}
}
4 changes: 4 additions & 0 deletions types/typings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"name": "vue",
"main": "index.d.ts"
}
66 changes: 66 additions & 0 deletions types/vnode.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { Vue } from "./vue.d";

export type VNodeChildren = VNodeChildrenArrayContents | string;
export interface VNodeChildrenArrayContents {
[x: number]: VNode | string | VNodeChildren;
}

export interface VNode {
tag?: string;
data?: VNodeData;
children?: VNode[];
text?: string;
elm?: Node;
ns?: string;
context?: Vue;
key?: string | number;
componentOptions?: VNodeComponentOptions;
child?: Vue;
parent?: VNode;
raw?: boolean;
isStatic?: boolean;
isRootInsert: boolean;
isComment: boolean;
}

export interface VNodeComponentOptions {
Ctor: Vue;
propsData?: Object;
listeners?: Object;
children?: VNodeChildren;
tag?: string;
}

export interface VNodeData {
key?: string | number;
slot?: string;
ref?: string;
tag?: string;
staticClass?: string;
class?: any;
style?: Object[] | Object;
props?: { [key: string]: any };
attrs?: { [key: string]: any };
domProps?: { [key: string]: any };
hook?: { [key: string]: Function };
on?: { [key: string]: Function | Function[] };
nativeOn?: { [key: string]: Function | Function[] };
transition?: Object;
transitionInjected?: boolean;
show?: boolean;
inlineTemplate?: {
render: Function;
staticRenderFns: Function[];
};
directives?: VNodeDirective[];
keepAlive?: boolean;
}

export interface VNodeDirective {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might be wrong, but is it desirable to mark VNodeDirective's field as readonly, according to the doc

name: string;
value: any;
oldValue: any;
expression: any;
arg: string;
modifiers: { [key: string]: boolean };
}
Loading