refactor: types refactor

This commit is contained in:
Evan You 2018-10-08 18:09:13 -04:00
parent ba918b5afe
commit d22b71b27e
11 changed files with 74 additions and 64 deletions

View File

@ -6,9 +6,7 @@ describe('attribute fallthrough', () => {
const nativeClick = jest.fn() const nativeClick = jest.fn()
const childUpdated = jest.fn() const childUpdated = jest.fn()
class Hello extends Component<{ class Hello extends Component<{}, { count: number }> {
count: number
}> {
data() { data() {
return { return {
count: 0 count: 0
@ -29,7 +27,7 @@ describe('attribute fallthrough', () => {
} }
} }
class Child extends Component<{}, { foo: number }> { class Child extends Component<{ foo: number }> {
updated() { updated() {
childUpdated() childUpdated()
} }
@ -75,7 +73,7 @@ describe('attribute fallthrough', () => {
const nativeClick = jest.fn() const nativeClick = jest.fn()
const childUpdated = jest.fn() const childUpdated = jest.fn()
class Hello extends Component<{ count: number }> { class Hello extends Component<{}, { count: number }> {
data() { data() {
return { return {
count: 0 count: 0
@ -96,7 +94,7 @@ describe('attribute fallthrough', () => {
} }
} }
class Child extends Component<{}, { foo: number }> { class Child extends Component<{ foo: number }> {
static options = { static options = {
props: { props: {
foo: Number foo: Number
@ -149,7 +147,7 @@ describe('attribute fallthrough', () => {
const childUpdated = jest.fn() const childUpdated = jest.fn()
const grandChildUpdated = jest.fn() const grandChildUpdated = jest.fn()
class Hello extends Component<{ count: number }> { class Hello extends Component<{}, { count: number }> {
data() { data() {
return { return {
count: 0 count: 0
@ -184,7 +182,7 @@ describe('attribute fallthrough', () => {
} }
} }
class GrandChild extends Component<{}, { foo: number }> { class GrandChild extends Component<{ foo: number }> {
static options = { static options = {
props: { props: {
foo: Number foo: Number

View File

@ -13,42 +13,40 @@ import { ErrorTypes } from './errorHandling'
type Flatten<T> = { [K in keyof T]: T[K] } type Flatten<T> = { [K in keyof T]: T[K] }
export type RenderFunction<P = {}> = (
props: P,
slots: Slots,
attrs: Data
) => any
export interface ComponentClass extends Flatten<typeof InternalComponent> { export interface ComponentClass extends Flatten<typeof InternalComponent> {
new <D = {}, P = {}>(): D & P & MountedComponent<D, P> new <P extends object = {}, D extends object = {}>(): MergedComponent<P, D>
} }
export interface FunctionalComponent<P = {}> extends RenderFunction<P> { export type MergedComponent<P, D> = D & P & MountedComponent<P, D>
export interface FunctionalComponent<P = {}> {
(props: Readonly<P>, slots: Slots, attrs: Data): any
pure?: boolean pure?: boolean
props?: ComponentPropsOptions<P> props?: ComponentPropsOptions<P>
inheritAttrs?: boolean inheritAttrs?: boolean
displayName?: string
} }
export type ComponentType = ComponentClass | FunctionalComponent export type ComponentType = ComponentClass | FunctionalComponent
// this interface is merged with the class type // this interface is merged with the class type
// to represent a mounted component // to represent a mounted component
export interface MountedComponent<D = {}, P = {}> extends InternalComponent { export interface MountedComponent<P = {}, D = {}> extends InternalComponent {
$vnode: MountedVNode $vnode: MountedVNode
$data: D $data: D
$props: P $props: Readonly<P>
$attrs: Data $attrs: Data
$computed: Data $computed: Data
$slots: Slots $slots: Slots
$root: MountedComponent $root: MountedComponent
$children: MountedComponent[] $children: MountedComponent[]
$options: ComponentOptions<D, P> $options: ComponentOptions<P, D>
render(props: P, slots: Slots, attrs: Data): any data?(): Partial<D>
render(props: Readonly<P>, slots: Slots, attrs: Data): any
renderError?(e: Error): any renderError?(e: Error): any
renderTracked?(e: DebuggerEvent): void renderTracked?(e: DebuggerEvent): void
renderTriggered?(e: DebuggerEvent): void renderTriggered?(e: DebuggerEvent): void
data?(): Partial<D>
beforeCreate?(): void beforeCreate?(): void
created?(): void created?(): void
beforeMount?(): void beforeMount?(): void
@ -120,16 +118,15 @@ class InternalComponent {
} }
$watch( $watch(
this: MountedComponent,
keyOrFn: string | (() => any), keyOrFn: string | (() => any),
cb: (newValue: any, oldValue: any) => void, cb: (newValue: any, oldValue: any) => void,
options?: WatchOptions options?: WatchOptions
) { ) {
return setupWatcher(this, keyOrFn, cb, options) return setupWatcher(this as any, keyOrFn, cb, options)
} }
// eventEmitter interface // eventEmitter interface
$on(this: MountedComponent, event: string, fn: Function): MountedComponent { $on(event: string, fn: Function): this {
if (Array.isArray(event)) { if (Array.isArray(event)) {
for (let i = 0; i < event.length; i++) { for (let i = 0; i < event.length; i++) {
this.$on(event[i], fn) this.$on(event[i], fn)
@ -141,7 +138,7 @@ class InternalComponent {
return this return this
} }
$once(this: MountedComponent, event: string, fn: Function): MountedComponent { $once(event: string, fn: Function): this {
const onceFn = (...args: any[]) => { const onceFn = (...args: any[]) => {
this.$off(event, onceFn) this.$off(event, onceFn)
fn.apply(this, args) fn.apply(this, args)
@ -150,11 +147,7 @@ class InternalComponent {
return this.$on(event, onceFn) return this.$on(event, onceFn)
} }
$off( $off(event?: string, fn?: Function): this {
this: MountedComponent,
event?: string,
fn?: Function
): MountedComponent {
if (this._events) { if (this._events) {
if (!event && !fn) { if (!event && !fn) {
this._events = null this._events = null
@ -180,11 +173,7 @@ class InternalComponent {
return this return this
} }
$emit( $emit(name: string, ...payload: any[]): this {
this: MountedComponent,
name: string,
...payload: any[]
): MountedComponent {
const parentData = const parentData =
(this.$parentVNode && this.$parentVNode.data) || EMPTY_OBJ (this.$parentVNode && this.$parentVNode.data) || EMPTY_OBJ
const parentListener = const parentListener =

View File

@ -1,14 +1,22 @@
import { MountedComponent, RenderFunction } from './component' import { MergedComponent, MountedComponent } from './component'
import { Slots } from './vdom'
export type Data = Record<string, any> export type Data = Record<string, any>
export interface ComponentOptions<D = Data, P = Data> { export interface ComponentOptions<
data?: () => Partial<D> P = {},
D = {},
M = {},
C = {},
This = MergedComponent<P, D> & M & C
> {
data?: (this: This) => Partial<D>
props?: ComponentPropsOptions<P> props?: ComponentPropsOptions<P>
computed?: ComponentComputedOptions<D, P> computed?: ComponentComputedOptions<This>
watch?: ComponentWatchOptions<D, P> watch?: ComponentWatchOptions<This>
render?: RenderFunction<P> render?: (this: This, props: Readonly<P>, slots: Slots, attrs: Data) => any
inheritAttrs?: boolean inheritAttrs?: boolean
displayName?: string
// TODO other options // TODO other options
readonly [key: string]: any readonly [key: string]: any
} }
@ -30,24 +38,28 @@ export interface PropOptions<T = any> {
validator?(value: T): boolean validator?(value: T): boolean
} }
export interface ComponentComputedOptions<D = Data, P = Data> { export interface ComponentComputedOptions<This = MountedComponent> {
[key: string]: (this: MountedComponent<D, P> & D & P, c: any) => any [key: string]: (this: This, c: any) => any
} }
export interface ComponentWatchOptions<D = Data, P = Data> { export interface ComponentWatchOptions<This = MountedComponent> {
[key: string]: ComponentWatchOption<MountedComponent<D, P> & D & P> [key: string]: ComponentWatchOption<This>
} }
export type ComponentWatchOption<C = any> = export type ComponentWatchOption<This = MountedComponent> =
| WatchHandler<C> | WatchHandler<This>
| WatchHandler<C>[] | WatchHandler<This>[]
| WatchOptionsWithHandler<C> | WatchOptionsWithHandler<This>
| string | string
export type WatchHandler<C = any> = (this: C, val: any, oldVal: any) => void export type WatchHandler<This = any> = (
this: This,
val: any,
oldVal: any
) => void
export interface WatchOptionsWithHandler<C = any> extends WatchOptions { export interface WatchOptionsWithHandler<This = any> extends WatchOptions {
handler: WatchHandler<C> handler: WatchHandler<This>
} }
export interface WatchOptions { export interface WatchOptions {

View File

@ -40,7 +40,7 @@ export function createAsyncComponent(
error: errorComp error: errorComp
} = options } = options
return class AsyncContainer extends Component<AsyncContainerData> { return class AsyncContainer extends Component<{}, AsyncContainerData> {
data() { data() {
return { return {
comp: null, comp: null,

View File

@ -8,7 +8,7 @@ interface ProviderProps {
value: any value: any
} }
export class Provide extends Component<{}, ProviderProps> { export class Provide extends Component<ProviderProps> {
updateValue() { updateValue() {
// TS doesn't allow symbol as index :/ // TS doesn't allow symbol as index :/
// https://github.com/Microsoft/TypeScript/issues/24587 // https://github.com/Microsoft/TypeScript/issues/24587

View File

@ -15,7 +15,7 @@ type Cache = Map<CacheKey, VNode>
export const KeepAliveSymbol = Symbol() export const KeepAliveSymbol = Symbol()
export class KeepAlive extends Component<{}, KeepAliveProps> { export class KeepAlive extends Component<KeepAliveProps> {
cache: Cache = new Map() cache: Cache = new Map()
keys: Set<CacheKey> = new Set() keys: Set<CacheKey> = new Set()

View File

@ -27,8 +27,9 @@ export interface VNode {
slots: Slots | null slots: Slots | null
// only on mounted nodes // only on mounted nodes
el: RenderNode | null el: RenderNode | null
// only on mounted component root nodes // only on mounted component nodes that is also a root node (HOCs)
// points to component node in parent tree // points to parent component's placeholder vnode
// this is used to update vnode.el for nested HOCs.
parentVNode: VNode | null parentVNode: VNode | null
} }
@ -56,12 +57,12 @@ export type Key = string | number
export type Ref = (t: RenderNode | MountedComponent | null) => void export type Ref = (t: RenderNode | MountedComponent | null) => void
export interface Slots {
[name: string]: Slot
}
export type Slot = (...args: any[]) => VNode[] export type Slot = (...args: any[]) => VNode[]
export type Slots = Readonly<{
[name: string]: Slot
}>
export function createVNode( export function createVNode(
flags: VNodeFlags, flags: VNodeFlags,
tag: string | FunctionalComponent | ComponentClass | RenderNode | null, tag: string | FunctionalComponent | ComponentClass | RenderNode | null,

View File

@ -7,7 +7,7 @@ describe('2.x compat build', async () => {
const root = document.createElement('div') const root = document.createElement('div')
document.body.appendChild(root) document.body.appendChild(root)
const instance = new Vue({ const instance = new Vue<any>({
data() { data() {
return { count: 0 } return { count: 0 }
}, },

View File

@ -6,13 +6,19 @@ import {
ComponentOptions, ComponentOptions,
createComponentInstance createComponentInstance
} from '@vue/renderer-dom' } from '@vue/renderer-dom'
import { MergedComponent } from '../../core/src/component'
class Vue extends Component { class Vue<
P extends object = {},
D extends object = {},
M extends object = {},
C extends object = {}
> extends Component {
static h = h static h = h
static render = render static render = render
static nextTick = nextTick static nextTick = nextTick
constructor(options: ComponentOptions & { el?: any }) { constructor(options: ComponentOptions<P, D, M, C> & { el?: any }) {
super() super()
if (!options) { if (!options) {
return return
@ -43,5 +49,9 @@ class Vue extends Component {
} }
} }
interface Vue<P, D, M, C> {
$mount(el: any): MergedComponent<P, D> & M & C
}
export default Vue export default Vue
export * from '@vue/renderer-dom' export * from '@vue/renderer-dom'