Skip to content
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

Add typescript definition #3509

Closed
wants to merge 11 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@
"dev:compiler": "webpack --watch --config build/webpack.compiler.dev.config.js",
"build": "NODE_ENV=production node build/build.js",
"build:ssr": "npm run build -- vue.common.js,vue-server-renderer",
"test": "npm run lint && flow check && npm run test:cover && npm run test:e2e -- --env phantomjs && npm run test:ssr",
"test": "npm run lint && flow check && npm run test:cover && npm run test:e2e -- --env phantomjs && npm run test:ssr && npm run test:types",
"test:unit": "NODE_ENV=development karma start build/karma.unit.config.js",
"test:cover": "NODE_ENV=development karma start build/karma.cover.config.js",
"test:e2e": "npm run build -- vue.js && node test/e2e/runner.js",
"test:ssr": "npm run build:ssr && NODE_ENV=development VUE_ENV=server jasmine JASMINE_CONFIG_PATH=test/ssr/jasmine.json",
"test:sauce": "npm run sauce -- 0 && npm run sauce -- 1 && npm run sauce -- 2",
"test:types": "tsc -p ./types/test/tsconfig.json",
"lint": "eslint src build test",
"flow": "flow check",
"sauce": "NODE_ENV=development SAUCE=true karma start build/karma.sauce.config.js",
Expand Down
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;
74 changes: 74 additions & 0 deletions types/options.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
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)[];

beforeCreate?(): void;
created?(): void;
beforeDestroy?(): void;
destroyed?(): 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;
}
8 changes: 8 additions & 0 deletions types/plugin.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Vue as _Vue } from "./vue.d";

export type PluginFunction<T> = (Vue: typeof _Vue, options?: T) => void;

export interface PluginObject<T> {
install: PluginFunction<T>;
[key: string]: any;
}
136 changes: 136 additions & 0 deletions types/test/options-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
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", {
attrs: {
id: "foo"
},
props: {
myProp: "bar"
},
domProps: {
innerHTML: "baz"
},
on: {
click: new Function
},
nativeOn: {
click: new Function
},
class: {
foo: true,
bar: false
},
style: {
color: 'red',
fontSize: '14px'
},
key: 'myKey',
ref: 'myRef'
}, [
createElement("div", {}, "message"),
"message",
[createElement("div", {}, "message")]
]);
},
staticRenderFns: [],

beforeCreate() {},
created() {},
beforeDestroy() {},
destroyed() {},
beforeMount() {},
mounted() {},
beforeUpdate() {},
updated() {},

directives: {
a: {
bind() {},
update() {},
componentMounted() {},
unbind() {}
},
b(el, binding, vnode, oldVnode) {
el.textContent;

binding.name;
binding.value;
binding.oldValue;
binding.expression;
binding.arg;
binding.modifiers["modifier"];
}
},
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);
20 changes: 20 additions & 0 deletions types/test/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"noImplicitAny": true,
"strictNullChecks": true,
"noEmit": 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"
}
Loading