types: improve h() and TSX type inference

- Should allow extraneous props as attrs in TSX
- Should check props when using constructor type returned by
  createComponent() in h()
This commit is contained in:
Evan You 2019-10-31 12:43:05 -04:00
parent b114cdf6ee
commit 3a6dcd3aba
4 changed files with 48 additions and 16 deletions

View File

@ -67,7 +67,14 @@ test('createComponent type inference', () => {
}
})
// test TSX props inference
;<MyComponent a={1} b="foo" dd={['foo']} ddd={['foo']} />
;<MyComponent
a={1}
b="foo"
dd={['foo']}
ddd={['foo']}
// should allow extraneous as attrs
class="bar"
/>
})
test('type inference w/ optional props declaration', () => {

View File

@ -9,13 +9,31 @@ import { SetupContext, RenderFunction } from './component'
import { ComponentPublicInstance } from './componentProxy'
import { ExtractPropTypes } from './componentProps'
import { isFunction } from '@vue/shared'
import { Ref } from '@vue/reactivity'
interface BaseProps {
[key: string]: any
key?: string | number
ref?: string | Ref | Function
}
// overload 1: direct setup function
// (uses user defined props interface)
// __isConstructor: true is a type-only differentiator to avoid returned
// constructor type from being matched as an options object in h()
export function createComponent<Props, RawBindings = object>(
setup: (props: Props, ctx: SetupContext) => RawBindings | RenderFunction
): {
new (): ComponentPublicInstance<Props, RawBindings>
__isConstructor: true
new (): ComponentPublicInstance<
Props,
RawBindings,
{},
{},
{},
// public props
BaseProps & Props
>
}
// overload 2: object format with no props
@ -30,11 +48,19 @@ export function createComponent<
>(
options: ComponentOptionsWithoutProps<Props, RawBindings, D, C, M>
): {
new (): ComponentPublicInstance<Props, RawBindings, D, C, M>
__isConstructor: true
new (): ComponentPublicInstance<
Props,
RawBindings,
D,
C,
M,
BaseProps & Props
>
}
// overload 3: object format with array props declaration
// props inferred as { [key in PropNames]?: unknown }
// props inferred as { [key in PropNames]?: any }
// return type is for Vetur and TSX support
export function createComponent<
PropNames extends string,
@ -45,13 +71,9 @@ export function createComponent<
>(
options: ComponentOptionsWithArrayProps<PropNames, RawBindings, D, C, M>
): {
new (): ComponentPublicInstance<
{ [key in PropNames]?: unknown },
RawBindings,
D,
C,
M
>
__isConstructor: true
// array props technically doesn't place any contraints on props in TSX
new (): ComponentPublicInstance<BaseProps, RawBindings, D, C, M>
}
// overload 4: object format with object props declaration
@ -65,14 +87,15 @@ export function createComponent<
>(
options: ComponentOptionsWithObjectProps<PropsOptions, RawBindings, D, C, M>
): {
__isConstructor: true
// for Vetur and TSX support
new (): ComponentPublicInstance<
ExtractPropTypes<PropsOptions>,
ExtractPropTypes<PropsOptions, false>,
RawBindings,
D,
C,
M,
ExtractPropTypes<PropsOptions, false>
BaseProps & ExtractPropTypes<PropsOptions, false>
>
}

View File

@ -65,6 +65,10 @@ export interface ComponentOptionsBase<
components?: Record<string, Component>
directives?: Record<string, Directive>
inheritAttrs?: boolean
// type-only differentiator to separate OptionWihtoutProps from a constructor
// type returned by createComponent()
__isConstructor?: never
}
export type ComponentOptionsWithoutProps<

View File

@ -133,9 +133,7 @@ export function h<P>(
): VNode
export function h<P extends string>(
type: ComponentOptionsWithArrayProps<P>,
// TODO for now this doesn't really do anything, but it would become useful
// if we make props required by default
props?: (RawProps & { [key in P]?: any }) | null,
props?: RawProps | null,
children?: RawChildren | RawSlots
): VNode
export function h<P>(