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

Further improve Vue type declarations for canonical usage #6391

Merged
merged 56 commits into from Oct 6, 2017
Commits
Jump to file or symbol
Failed to load files and symbols.
+394 −157
Diff settings

Always

Just for now

View
@@ -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.48.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.20.5",
View
@@ -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";
View
@@ -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;

This comment has been minimized.

@ktsn

ktsn Aug 20, 2017

Member

Looks like ThisType of computed, watch, render and lifecycle hooks will be invalid if the users annotate their component options with as ComponentOptions<Something>.
Can we deal with in that case too?

@ktsn

ktsn Aug 20, 2017

Member

Looks like ThisType of computed, watch, render and lifecycle hooks will be invalid if the users annotate their component options with as ComponentOptions<Something>.
Can we deal with in that case too?

This comment has been minimized.

@HerringtonDarkholme

HerringtonDarkholme Aug 20, 2017

Member

Type assertion like { opt: ...} as Option would not bring type inference to the declaration.
Users have to annotate Vue.extend or casting this.

@HerringtonDarkholme

HerringtonDarkholme Aug 20, 2017

Member

Type assertion like { opt: ...} as Option would not bring type inference to the declaration.
Users have to annotate Vue.extend or casting this.

This comment has been minimized.

@ktsn

ktsn Aug 20, 2017

Member

I see. Then I think we should stated about this in docs for migration 🙂

@ktsn

ktsn Aug 20, 2017

Member

I see. Then I think we should stated about this in docs for migration 🙂

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,49 +97,64 @@ 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;
parent: Vue;
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>[];

This comment has been minimized.

@ktsn

ktsn Aug 20, 2017

Member

This is clever declaration :D

@ktsn

ktsn Aug 20, 2017

Member

This is clever declaration :D

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

This comment has been minimized.

@ktsn

ktsn Aug 20, 2017

Member

We should keep this line to test ComponentOptions augmentation?

@ktsn

ktsn Aug 20, 2017

Member

We should keep this line to test ComponentOptions augmentation?

This comment has been minimized.

@HerringtonDarkholme

HerringtonDarkholme Aug 20, 2017

Member

Oops, unintentional deletion during refactoring. Thanks for pointing out!

@HerringtonDarkholme

HerringtonDarkholme Aug 20, 2017

Member

Oops, unintentional deletion during refactoring. Thanks for pointing out!

foo: "foo",
methods: {
foo() {
this.a = false;
}
},
computed: {
BAR(): string {
return this.bar.toUpperCase();
}
}
});
vm.$instanceProperty;
Oops, something went wrong.
ProTip! Use n and p to navigate between commits in a pull request.