feat(types): map declared emits to onXXX props in inferred prop types (#3926)
This commit is contained in:
parent
35cc7b0d31
commit
69344ff1ae
@ -1,3 +1,5 @@
|
|||||||
semi: false
|
semi: false
|
||||||
singleQuote: true
|
singleQuote: true
|
||||||
printWidth: 80
|
printWidth: 80
|
||||||
|
trailingComma: 'none'
|
||||||
|
arrowParens: 'avoid'
|
||||||
|
@ -68,7 +68,7 @@
|
|||||||
"lint-staged": "^10.2.10",
|
"lint-staged": "^10.2.10",
|
||||||
"minimist": "^1.2.0",
|
"minimist": "^1.2.0",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"prettier": "~1.14.0",
|
"prettier": "^2.3.1",
|
||||||
"puppeteer": "^10.0.0",
|
"puppeteer": "^10.0.0",
|
||||||
"rollup": "~2.38.5",
|
"rollup": "~2.38.5",
|
||||||
"rollup-plugin-node-builtins": "^2.1.2",
|
"rollup-plugin-node-builtins": "^2.1.2",
|
||||||
|
@ -18,7 +18,7 @@ import {
|
|||||||
ComponentPropsOptions,
|
ComponentPropsOptions,
|
||||||
ExtractDefaultPropTypes
|
ExtractDefaultPropTypes
|
||||||
} from './componentProps'
|
} from './componentProps'
|
||||||
import { EmitsOptions } from './componentEmits'
|
import { EmitsOptions, EmitsToProps } from './componentEmits'
|
||||||
import { isFunction } from '@vue/shared'
|
import { isFunction } from '@vue/shared'
|
||||||
import { VNodeProps } from './vnode'
|
import { VNodeProps } from './vnode'
|
||||||
import {
|
import {
|
||||||
@ -41,7 +41,7 @@ export type DefineComponent<
|
|||||||
E extends EmitsOptions = Record<string, any>,
|
E extends EmitsOptions = Record<string, any>,
|
||||||
EE extends string = string,
|
EE extends string = string,
|
||||||
PP = PublicProps,
|
PP = PublicProps,
|
||||||
Props = Readonly<ExtractPropTypes<PropsOrPropOptions>>,
|
Props = Readonly<ExtractPropTypes<PropsOrPropOptions>> & EmitsToProps<E>,
|
||||||
Defaults = ExtractDefaultPropTypes<PropsOrPropOptions>
|
Defaults = ExtractDefaultPropTypes<PropsOrPropOptions>
|
||||||
> = ComponentPublicInstanceConstructor<
|
> = ComponentPublicInstanceConstructor<
|
||||||
CreateComponentPublicInstance<
|
CreateComponentPublicInstance<
|
||||||
@ -102,7 +102,7 @@ export function defineComponent<
|
|||||||
EE extends string = string
|
EE extends string = string
|
||||||
>(
|
>(
|
||||||
options: ComponentOptionsWithoutProps<
|
options: ComponentOptionsWithoutProps<
|
||||||
Props,
|
Props & EmitsToProps<E>,
|
||||||
RawBindings,
|
RawBindings,
|
||||||
D,
|
D,
|
||||||
C,
|
C,
|
||||||
|
@ -31,22 +31,38 @@ export type ObjectEmitsOptions = Record<
|
|||||||
string,
|
string,
|
||||||
((...args: any[]) => any) | null
|
((...args: any[]) => any) | null
|
||||||
>
|
>
|
||||||
|
|
||||||
export type EmitsOptions = ObjectEmitsOptions | string[]
|
export type EmitsOptions = ObjectEmitsOptions | string[]
|
||||||
|
|
||||||
|
export type EmitsToProps<T extends EmitsOptions> = T extends string[]
|
||||||
|
? {
|
||||||
|
[K in string & `on${Capitalize<T[number]>}`]?: (...args: any[]) => any
|
||||||
|
}
|
||||||
|
: T extends ObjectEmitsOptions
|
||||||
|
? {
|
||||||
|
[K in string &
|
||||||
|
`on${Capitalize<string & keyof T>}`]?: K extends `on${infer C}`
|
||||||
|
? T[Uncapitalize<C>] extends null
|
||||||
|
? (...args: any[]) => any
|
||||||
|
: T[Uncapitalize<C>]
|
||||||
|
: never
|
||||||
|
}
|
||||||
|
: {}
|
||||||
|
|
||||||
export type EmitFn<
|
export type EmitFn<
|
||||||
Options = ObjectEmitsOptions,
|
Options = ObjectEmitsOptions,
|
||||||
Event extends keyof Options = keyof Options
|
Event extends keyof Options = keyof Options
|
||||||
> = Options extends Array<infer V>
|
> = Options extends Array<infer V>
|
||||||
? (event: V, ...args: any[]) => void
|
? (event: V, ...args: any[]) => void
|
||||||
: {} extends Options // if the emit is empty object (usually the default value for emit) should be converted to function
|
: {} extends Options // if the emit is empty object (usually the default value for emit) should be converted to function
|
||||||
? (event: string, ...args: any[]) => void
|
? (event: string, ...args: any[]) => void
|
||||||
: UnionToIntersection<
|
: UnionToIntersection<
|
||||||
{
|
{
|
||||||
[key in Event]: Options[key] extends ((...args: infer Args) => any)
|
[key in Event]: Options[key] extends (...args: infer Args) => any
|
||||||
? (event: key, ...args: Args) => void
|
? (event: key, ...args: Args) => void
|
||||||
: (event: key, ...args: any[]) => void
|
: (event: key, ...args: any[]) => void
|
||||||
}[Event]
|
}[Event]
|
||||||
>
|
>
|
||||||
|
|
||||||
export function emit(
|
export function emit(
|
||||||
instance: ComponentInternalInstance,
|
instance: ComponentInternalInstance,
|
||||||
|
@ -51,7 +51,7 @@ import {
|
|||||||
ExtractPropTypes,
|
ExtractPropTypes,
|
||||||
ExtractDefaultPropTypes
|
ExtractDefaultPropTypes
|
||||||
} from './componentProps'
|
} from './componentProps'
|
||||||
import { EmitsOptions } from './componentEmits'
|
import { EmitsOptions, EmitsToProps } from './componentEmits'
|
||||||
import { Directive } from './directives'
|
import { Directive } from './directives'
|
||||||
import {
|
import {
|
||||||
CreateComponentPublicInstance,
|
CreateComponentPublicInstance,
|
||||||
@ -91,16 +91,18 @@ export interface ComponentCustomOptions {}
|
|||||||
export type RenderFunction = () => VNodeChild
|
export type RenderFunction = () => VNodeChild
|
||||||
|
|
||||||
type ExtractOptionProp<T> = T extends ComponentOptionsBase<
|
type ExtractOptionProp<T> = T extends ComponentOptionsBase<
|
||||||
infer P,
|
infer P, // Props
|
||||||
any,
|
any, // RawBindings
|
||||||
any,
|
any, // D
|
||||||
any,
|
any, // C
|
||||||
any,
|
any, // M
|
||||||
any,
|
any, // Mixin
|
||||||
any,
|
any, // Extends
|
||||||
any
|
any // EmitsOptions
|
||||||
>
|
>
|
||||||
? unknown extends P ? {} : P
|
? unknown extends P
|
||||||
|
? {}
|
||||||
|
: P
|
||||||
: {}
|
: {}
|
||||||
|
|
||||||
export interface ComponentOptionsBase<
|
export interface ComponentOptionsBase<
|
||||||
@ -114,8 +116,7 @@ export interface ComponentOptionsBase<
|
|||||||
E extends EmitsOptions,
|
E extends EmitsOptions,
|
||||||
EE extends string = string,
|
EE extends string = string,
|
||||||
Defaults = {}
|
Defaults = {}
|
||||||
>
|
> extends LegacyOptions<Props, D, C, M, Mixin, Extends>,
|
||||||
extends LegacyOptions<Props, D, C, M, Mixin, Extends>,
|
|
||||||
ComponentInternalOptions,
|
ComponentInternalOptions,
|
||||||
ComponentCustomOptions {
|
ComponentCustomOptions {
|
||||||
setup?: (
|
setup?: (
|
||||||
@ -220,9 +221,10 @@ export type ComponentOptionsWithoutProps<
|
|||||||
Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
|
Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||||
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||||
E extends EmitsOptions = EmitsOptions,
|
E extends EmitsOptions = EmitsOptions,
|
||||||
EE extends string = string
|
EE extends string = string,
|
||||||
|
PE = Props & EmitsToProps<E>
|
||||||
> = ComponentOptionsBase<
|
> = ComponentOptionsBase<
|
||||||
Props,
|
PE,
|
||||||
RawBindings,
|
RawBindings,
|
||||||
D,
|
D,
|
||||||
C,
|
C,
|
||||||
@ -235,7 +237,7 @@ export type ComponentOptionsWithoutProps<
|
|||||||
> & {
|
> & {
|
||||||
props?: undefined
|
props?: undefined
|
||||||
} & ThisType<
|
} & ThisType<
|
||||||
CreateComponentPublicInstance<{}, RawBindings, D, C, M, Mixin, Extends, E>
|
CreateComponentPublicInstance<PE, RawBindings, D, C, M, Mixin, Extends, E>
|
||||||
>
|
>
|
||||||
|
|
||||||
export type ComponentOptionsWithArrayProps<
|
export type ComponentOptionsWithArrayProps<
|
||||||
@ -248,7 +250,7 @@ export type ComponentOptionsWithArrayProps<
|
|||||||
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||||
E extends EmitsOptions = EmitsOptions,
|
E extends EmitsOptions = EmitsOptions,
|
||||||
EE extends string = string,
|
EE extends string = string,
|
||||||
Props = Readonly<{ [key in PropNames]?: any }>
|
Props = Readonly<{ [key in PropNames]?: any }> & EmitsToProps<E>
|
||||||
> = ComponentOptionsBase<
|
> = ComponentOptionsBase<
|
||||||
Props,
|
Props,
|
||||||
RawBindings,
|
RawBindings,
|
||||||
@ -285,7 +287,7 @@ export type ComponentOptionsWithObjectProps<
|
|||||||
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||||
E extends EmitsOptions = EmitsOptions,
|
E extends EmitsOptions = EmitsOptions,
|
||||||
EE extends string = string,
|
EE extends string = string,
|
||||||
Props = Readonly<ExtractPropTypes<PropsOptions>>,
|
Props = Readonly<ExtractPropTypes<PropsOptions>> & EmitsToProps<E>,
|
||||||
Defaults = ExtractDefaultPropTypes<PropsOptions>
|
Defaults = ExtractDefaultPropTypes<PropsOptions>
|
||||||
> = ComponentOptionsBase<
|
> = ComponentOptionsBase<
|
||||||
Props,
|
Props,
|
||||||
@ -365,7 +367,9 @@ export interface MethodOptions {
|
|||||||
export type ExtractComputedReturns<T extends any> = {
|
export type ExtractComputedReturns<T extends any> = {
|
||||||
[key in keyof T]: T[key] extends { get: (...args: any[]) => infer TReturn }
|
[key in keyof T]: T[key] extends { get: (...args: any[]) => infer TReturn }
|
||||||
? TReturn
|
? TReturn
|
||||||
: T[key] extends (...args: any[]) => infer TReturn ? TReturn : never
|
: T[key] extends (...args: any[]) => infer TReturn
|
||||||
|
? TReturn
|
||||||
|
: never
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ObjectWatchOptionItem = {
|
export type ObjectWatchOptionItem = {
|
||||||
@ -471,7 +475,7 @@ interface LegacyOptions<
|
|||||||
__differentiator?: keyof D | keyof C | keyof M
|
__differentiator?: keyof D | keyof C | keyof M
|
||||||
}
|
}
|
||||||
|
|
||||||
type MergedHook<T = (() => void)> = T | T[]
|
type MergedHook<T = () => void> = T | T[]
|
||||||
|
|
||||||
export type MergedComponentOptions = ComponentOptions &
|
export type MergedComponentOptions = ComponentOptions &
|
||||||
MergedComponentOptionsOverride
|
MergedComponentOptionsOverride
|
||||||
@ -679,8 +683,8 @@ export function applyOptions(instance: ComponentInternalInstance) {
|
|||||||
const get = isFunction(opt)
|
const get = isFunction(opt)
|
||||||
? opt.bind(publicThis, publicThis)
|
? opt.bind(publicThis, publicThis)
|
||||||
: isFunction(opt.get)
|
: isFunction(opt.get)
|
||||||
? opt.get.bind(publicThis, publicThis)
|
? opt.get.bind(publicThis, publicThis)
|
||||||
: NOOP
|
: NOOP
|
||||||
if (__DEV__ && get === NOOP) {
|
if (__DEV__ && get === NOOP) {
|
||||||
warn(`Computed property "${key}" has no getter.`)
|
warn(`Computed property "${key}" has no getter.`)
|
||||||
}
|
}
|
||||||
@ -688,12 +692,12 @@ export function applyOptions(instance: ComponentInternalInstance) {
|
|||||||
!isFunction(opt) && isFunction(opt.set)
|
!isFunction(opt) && isFunction(opt.set)
|
||||||
? opt.set.bind(publicThis)
|
? opt.set.bind(publicThis)
|
||||||
: __DEV__
|
: __DEV__
|
||||||
? () => {
|
? () => {
|
||||||
warn(
|
warn(
|
||||||
`Write operation failed: computed property "${key}" is readonly.`
|
`Write operation failed: computed property "${key}" is readonly.`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
: NOOP
|
: NOOP
|
||||||
const c = computed({
|
const c = computed({
|
||||||
get,
|
get,
|
||||||
set
|
set
|
||||||
@ -1006,10 +1010,11 @@ function mergeDataFn(to: any, from: any) {
|
|||||||
return from
|
return from
|
||||||
}
|
}
|
||||||
return function mergedDataFn(this: ComponentPublicInstance) {
|
return function mergedDataFn(this: ComponentPublicInstance) {
|
||||||
return (__COMPAT__ &&
|
return (
|
||||||
isCompatEnabled(DeprecationTypes.OPTIONS_DATA_MERGE, null)
|
__COMPAT__ && isCompatEnabled(DeprecationTypes.OPTIONS_DATA_MERGE, null)
|
||||||
? deepMergeData
|
? deepMergeData
|
||||||
: extend)(
|
: extend
|
||||||
|
)(
|
||||||
isFunction(to) ? to.call(this, this) : to,
|
isFunction(to) ? to.call(this, this) : to,
|
||||||
isFunction(from) ? from.call(this, this) : from
|
isFunction(from) ? from.call(this, this) : from
|
||||||
)
|
)
|
||||||
|
@ -469,6 +469,7 @@ describe('type inference w/ options API', () => {
|
|||||||
|
|
||||||
describe('with mixins', () => {
|
describe('with mixins', () => {
|
||||||
const MixinA = defineComponent({
|
const MixinA = defineComponent({
|
||||||
|
emits: ['bar'],
|
||||||
props: {
|
props: {
|
||||||
aP1: {
|
aP1: {
|
||||||
type: String,
|
type: String,
|
||||||
@ -523,6 +524,7 @@ describe('with mixins', () => {
|
|||||||
})
|
})
|
||||||
const MyComponent = defineComponent({
|
const MyComponent = defineComponent({
|
||||||
mixins: [MixinA, MixinB, MixinC, MixinD],
|
mixins: [MixinA, MixinB, MixinC, MixinD],
|
||||||
|
emits: ['click'],
|
||||||
props: {
|
props: {
|
||||||
// required should make property non-void
|
// required should make property non-void
|
||||||
z: {
|
z: {
|
||||||
@ -552,6 +554,9 @@ describe('with mixins', () => {
|
|||||||
setup(props) {
|
setup(props) {
|
||||||
expectType<string>(props.z)
|
expectType<string>(props.z)
|
||||||
// props
|
// props
|
||||||
|
expectType<((...args: any[]) => any) | undefined>(props.onClick)
|
||||||
|
// from Base
|
||||||
|
expectType<((...args: any[]) => any) | undefined>(props.onBar)
|
||||||
expectType<string>(props.aP1)
|
expectType<string>(props.aP1)
|
||||||
expectType<boolean | undefined>(props.aP2)
|
expectType<boolean | undefined>(props.aP2)
|
||||||
expectType<any>(props.bP1)
|
expectType<any>(props.bP1)
|
||||||
@ -561,6 +566,9 @@ describe('with mixins', () => {
|
|||||||
render() {
|
render() {
|
||||||
const props = this.$props
|
const props = this.$props
|
||||||
// props
|
// props
|
||||||
|
expectType<((...args: any[]) => any) | undefined>(props.onClick)
|
||||||
|
// from Base
|
||||||
|
expectType<((...args: any[]) => any) | undefined>(props.onBar)
|
||||||
expectType<string>(props.aP1)
|
expectType<string>(props.aP1)
|
||||||
expectType<boolean | undefined>(props.aP2)
|
expectType<boolean | undefined>(props.aP2)
|
||||||
expectType<any>(props.bP1)
|
expectType<any>(props.bP1)
|
||||||
@ -688,6 +696,7 @@ describe('with extends', () => {
|
|||||||
|
|
||||||
describe('extends with mixins', () => {
|
describe('extends with mixins', () => {
|
||||||
const Mixin = defineComponent({
|
const Mixin = defineComponent({
|
||||||
|
emits: ['bar'],
|
||||||
props: {
|
props: {
|
||||||
mP1: {
|
mP1: {
|
||||||
type: String,
|
type: String,
|
||||||
@ -706,6 +715,7 @@ describe('extends with mixins', () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
const Base = defineComponent({
|
const Base = defineComponent({
|
||||||
|
emits: ['foo'],
|
||||||
props: {
|
props: {
|
||||||
p1: Boolean,
|
p1: Boolean,
|
||||||
p2: {
|
p2: {
|
||||||
@ -731,6 +741,7 @@ describe('extends with mixins', () => {
|
|||||||
const MyComponent = defineComponent({
|
const MyComponent = defineComponent({
|
||||||
extends: Base,
|
extends: Base,
|
||||||
mixins: [Mixin],
|
mixins: [Mixin],
|
||||||
|
emits: ['click'],
|
||||||
props: {
|
props: {
|
||||||
// required should make property non-void
|
// required should make property non-void
|
||||||
z: {
|
z: {
|
||||||
@ -741,6 +752,11 @@ describe('extends with mixins', () => {
|
|||||||
render() {
|
render() {
|
||||||
const props = this.$props
|
const props = this.$props
|
||||||
// props
|
// props
|
||||||
|
expectType<((...args: any[]) => any) | undefined>(props.onClick)
|
||||||
|
// from Mixin
|
||||||
|
expectType<((...args: any[]) => any) | undefined>(props.onBar)
|
||||||
|
// from Base
|
||||||
|
expectType<((...args: any[]) => any) | undefined>(props.onFoo)
|
||||||
expectType<boolean | undefined>(props.p1)
|
expectType<boolean | undefined>(props.p1)
|
||||||
expectType<number>(props.p2)
|
expectType<number>(props.p2)
|
||||||
expectType<string>(props.z)
|
expectType<string>(props.z)
|
||||||
@ -879,6 +895,8 @@ describe('emits', () => {
|
|||||||
input: (b: string) => b.length > 1
|
input: (b: string) => b.length > 1
|
||||||
},
|
},
|
||||||
setup(props, { emit }) {
|
setup(props, { emit }) {
|
||||||
|
expectType<((n: number) => boolean) | undefined>(props.onClick)
|
||||||
|
expectType<((b: string) => boolean) | undefined>(props.onInput)
|
||||||
emit('click', 1)
|
emit('click', 1)
|
||||||
emit('input', 'foo')
|
emit('input', 'foo')
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
@ -931,6 +949,8 @@ describe('emits', () => {
|
|||||||
defineComponent({
|
defineComponent({
|
||||||
emits: ['foo', 'bar'],
|
emits: ['foo', 'bar'],
|
||||||
setup(props, { emit }) {
|
setup(props, { emit }) {
|
||||||
|
expectType<((...args: any[]) => any) | undefined>(props.onFoo)
|
||||||
|
expectType<((...args: any[]) => any) | undefined>(props.onBar)
|
||||||
emit('foo')
|
emit('foo')
|
||||||
emit('foo', 123)
|
emit('foo', 123)
|
||||||
emit('bar')
|
emit('bar')
|
||||||
@ -972,10 +992,9 @@ describe('emits', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('componentOptions setup should be `SetupContext`', () => {
|
describe('componentOptions setup should be `SetupContext`', () => {
|
||||||
expect<ComponentOptions['setup']>({} as (
|
expect<ComponentOptions['setup']>(
|
||||||
props: Record<string, any>,
|
{} as (props: Record<string, any>, ctx: SetupContext) => any
|
||||||
ctx: SetupContext
|
)
|
||||||
) => any)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('extract instance type', () => {
|
describe('extract instance type', () => {
|
||||||
|
@ -5630,10 +5630,10 @@ prelude-ls@~1.1.2:
|
|||||||
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
|
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
|
||||||
integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=
|
integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=
|
||||||
|
|
||||||
prettier@~1.14.0:
|
prettier@^2.3.1:
|
||||||
version "1.14.3"
|
version "2.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.14.3.tgz#90238dd4c0684b7edce5f83b0fb7328e48bd0895"
|
resolved "https://registry.npmjs.org/prettier/-/prettier-2.3.1.tgz#76903c3f8c4449bc9ac597acefa24dc5ad4cbea6"
|
||||||
integrity sha512-qZDVnCrnpsRJJq5nSsiHCE3BYMED2OtsI+cmzIzF1QIfqm5ALf8tEJcO27zV1gKNKRPdhjO0dNWnrzssDQ1tFg==
|
integrity sha512-p+vNbgpLjif/+D+DwAZAbndtRrR0md0MwfmOVN9N+2RgyACMT+7tfaRnT+WDPkqnuVwleyuBIG2XBxKDme3hPA==
|
||||||
|
|
||||||
pretty-format@^26.0.0, pretty-format@^26.6.2:
|
pretty-format@^26.0.0, pretty-format@^26.6.2:
|
||||||
version "26.6.2"
|
version "26.6.2"
|
||||||
|
Loading…
Reference in New Issue
Block a user