types: improve createElement typing

This commit is contained in:
Evan You 2018-10-12 19:49:41 -04:00
parent 93d724382e
commit 5257b366fd
8 changed files with 85 additions and 65 deletions

View File

@ -22,7 +22,6 @@
"rust",
"scss",
"text",
"typescriptreact",
"yml"
]
}

View File

@ -6,12 +6,8 @@ describe('attribute fallthrough', () => {
const click = jest.fn()
const childUpdated = jest.fn()
class Hello extends Component<{}, { count: number }> {
data() {
return {
count: 0
}
}
class Hello extends Component {
count: number = 0
inc() {
this.count++
click()
@ -27,7 +23,7 @@ describe('attribute fallthrough', () => {
}
}
class Child extends Component {
class Child extends Component<{ [key: string]: any }> {
updated() {
childUpdated()
}
@ -73,19 +69,15 @@ describe('attribute fallthrough', () => {
const click = jest.fn()
const childUpdated = jest.fn()
class Hello extends Component<{}, { count: number }> {
data() {
return {
count: 0
}
}
class Hello extends Component {
count = 0
inc() {
this.count++
click()
}
render() {
return h(Child, {
foo: 1,
foo: 123,
id: 'test',
class: 'c' + this.count,
style: { color: this.count ? 'red' : 'green' },
@ -94,7 +86,7 @@ describe('attribute fallthrough', () => {
}
}
class Child extends Component<{ foo: number }> {
class Child extends Component<{ [key: string]: any; foo: number }> {
static props = {
foo: Number
}
@ -148,12 +140,8 @@ describe('attribute fallthrough', () => {
const childUpdated = jest.fn()
const grandChildUpdated = jest.fn()
class Hello extends Component<{}, { count: number }> {
data() {
return {
count: 0
}
}
class Hello extends Component {
count = 0
inc() {
this.count++
click()
@ -169,7 +157,7 @@ describe('attribute fallthrough', () => {
}
}
class Child extends Component {
class Child extends Component<{ [key: string]: any; foo: number }> {
updated() {
childUpdated()
}
@ -178,7 +166,7 @@ describe('attribute fallthrough', () => {
}
}
class GrandChild extends Component<{ foo: number }> {
class GrandChild extends Component<{ [key: string]: any; foo: number }> {
static props = {
foo: Number
}

View File

@ -13,9 +13,9 @@ import { nextTick } from '@vue/scheduler'
import { ErrorTypes } from './errorHandling'
import { initializeComponentInstance } from './componentUtils'
export interface ComponentClass extends ComponentClassOptions {
export interface ComponentClass<P = {}> extends ComponentClassOptions {
options?: ComponentOptions
new <P = {}, D = {}>(): MergedComponent<P, D>
new <P = {}, D = {}>(props?: P): MergedComponent<P, D>
}
export type MergedComponent<P, D> = D & P & ComponentInstance<P, D>

View File

@ -1,18 +1,18 @@
import { ComponentInstance } from './component'
import { ComponentInstance, MergedComponent } from './component'
import { Slots } from './vdom'
export type Data = Record<string, any>
export interface ComponentClassOptions<This = ComponentInstance> {
props?: ComponentPropsOptions
export interface ComponentClassOptions<P = {}, This = ComponentInstance> {
props?: ComponentPropsOptions<P>
computed?: ComponentComputedOptions<This>
watch?: ComponentWatchOptions<This>
displayName?: string
}
export interface ComponentOptions<This = ComponentInstance>
extends ComponentClassOptions<This> {
data?(): object
export interface ComponentOptions<P = {}, D = {}, This = MergedComponent<P, D>>
extends ComponentClassOptions<P, This> {
data?(): D
render?: (this: This, props: Readonly<Data>, slots: Slots, attrs: Data) => any
// TODO other options
readonly [key: string]: any

View File

@ -2,7 +2,6 @@ import { ChildrenFlags } from './flags'
import {
ComponentClass,
FunctionalComponent,
Component,
ComponentInstance
} from './component'
import { ComponentOptions } from './componentOptions'
@ -14,7 +13,8 @@ import {
createFragment,
createPortal,
VNodeData,
BuiltInProps
BuiltInProps,
Key
} from './vdom'
import { isObservable } from '@vue/observer'
import { warn } from './warning'
@ -22,6 +22,14 @@ import { warn } from './warning'
export const Fragment = Symbol()
export const Portal = Symbol()
type RawChildType = VNode | string | number | boolean | null | undefined
export type RawSlots = {
[name: string]: () => RawChildrenType
}
export type RawChildrenType = RawChildType | RawChildType[]
export type ElementType =
| string
| FunctionalComponent
@ -30,10 +38,6 @@ export type ElementType =
| typeof Fragment
| typeof Portal
type RawChildType = VNode | string | number | boolean | null | undefined
export type RawChildrenType = RawChildType | RawChildType[]
interface VNodeFactories {
c: typeof createComponentVNode
e: typeof createElementVNode
@ -42,20 +46,60 @@ interface VNodeFactories {
p: typeof createPortal
}
interface createElement {
// This is used to differentiate the data object from
// vnodes and arrays
type Differ = { _isVNode?: never; [Symbol.iterator]?: never }
type OptionsComponent<P> =
| (ComponentOptions<P> & { template: string })
| (ComponentOptions<P> & { render: Function })
interface createElement extends VNodeFactories {
// element
(tag: string, data?: VNodeData, children?: any): VNode
(
tag: string,
// TODO support native element properties
data?: VNodeData & Differ | null,
children?: RawChildrenType | RawSlots
): VNode
(tag: string, children?: RawChildrenType): VNode
// fragment
(
tag: typeof Fragment,
data?: ({ key?: Key } & Differ) | null,
children?: RawChildrenType | RawSlots
): VNode
(tag: typeof Fragment, children?: RawChildrenType): VNode
// portal
(
tag: typeof Portal,
data?: ({ target: any } & BuiltInProps & Differ) | null,
children?: RawChildrenType | RawSlots
): VNode
(tag: typeof Portal, children?: RawChildrenType): VNode
// object
<P>(
tag: OptionsComponent<P>,
data?: (P & BuiltInProps & Differ) | null,
children?: RawChildrenType | RawSlots
): VNode
<P>(tag: OptionsComponent<P>, children?: RawChildrenType): VNode
// functional
<P>(
tag: FunctionalComponent<P>,
data?: P & BuiltInProps | null,
children?: any
data?: (P & BuiltInProps & Differ) | null,
children?: RawChildrenType | RawSlots
): VNode
// stateful
<P>(tag: FunctionalComponent<P>, children?: RawChildrenType): VNode
// class
<P, T extends ComponentInstance<P>>(
tag: new () => T & { $props: P },
data?: P & BuiltInProps | null,
children?: any
data?: (P & BuiltInProps & Differ) | null,
children?: RawChildrenType | RawSlots
): VNode
<P, T extends ComponentInstance<P>>(
tag: new () => T & { $props: P },
children?: RawChildrenType
): VNode
}
@ -138,7 +182,7 @@ export const h = ((tag: ElementType, data?: any, children?: any): VNode => {
ref
)
}
}) as createElement & VNodeFactories
}) as createElement
h.c = createComponentVNode
h.e = createElementVNode

View File

@ -1,16 +1,6 @@
import { ComponentInstance, ComponentType } from '../component'
import { ComponentInstance } from '../component'
import { ComponentOptions } from '../componentOptions'
import { RawVNodeChildren, VNodeData } from '../vdom'
export interface Mixin extends ComponentOptions {}
export function applyMixins(Component: ComponentInstance, mixins: Mixin[]) {}
export function h(tag: ComponentType | string, data: RawVNodeChildren): object
export function h(
tag: ComponentType | string,
data: VNodeData,
children: RawVNodeChildren
): object {
return {}
}

View File

@ -6,7 +6,7 @@ import {
import { VNodeFlags, ChildrenFlags } from './flags'
import { createComponentClassFromOptions } from './componentUtils'
import { normalizeClass, normalizeStyle, handlersRE, EMPTY_OBJ } from './utils'
import { ElementType, RawChildrenType } from './h'
import { RawChildrenType, RawSlots } from './h'
// Vue core is platform agnostic, so we are not using Element for "DOM" nodes.
export interface RenderNode {
@ -47,7 +47,9 @@ export interface BuiltInProps {
slots?: RawSlots | null
}
export type VNodeData = Record<string, any> & BuiltInProps
export type VNodeData = {
[key: string]: any
} & BuiltInProps
export type VNodeChildren =
| VNode[] // ELEMENT | PORTAL
@ -65,10 +67,6 @@ export type Slots = Readonly<{
[name: string]: Slot
}>
export type RawSlots = {
[name: string]: () => RawChildrenType
}
export function createVNode(
flags: VNodeFlags,
tag: string | FunctionalComponent | ComponentClass | RenderNode | null,

View File

@ -7,8 +7,9 @@ import { patchEvent } from './modules/events'
export const onRE = /^on/
// value, checked, selected & muted are always patched as properties
const domPropsRE = /^domProps|^(?:value|checked|selected|muted)$/
// value, checked, selected & muted
// plus anything with upperCase letter in it are always patched as properties
const domPropsRE = /\W|^(?:value|checked|selected|muted)$/
export function patchData(
el: Element,