`Reactive Set contains both the raw and reactive`
).toHaveBeenWarned()
})
-
+
it('thisArg', () => {
- const raw = new Set([ 'value' ])
+ const raw = new Set(['value'])
const proxy = reactive(raw)
const thisArg = {}
let count = 0
- proxy.forEach(function (this :{}, value, _, set) {
+ proxy.forEach(function(this: {}, value, _, set) {
++count
expect(this).toBe(thisArg)
expect(value).toBe('value')
}
const res = Reflect.get(target, key, receiver)
- if (isSymbol(key) && builtInSymbols.has(key) || key === '__proto__') {
+ if ((isSymbol(key) && builtInSymbols.has(key)) || key === '__proto__') {
return res
}
}
}
const mixinB = {
+ props: {
+ bP: {
+ type: String
+ }
+ },
data() {
return {
b: 2
calls.push('mixinB created')
expect(this.a).toBe(1)
expect(this.b).toBe(2)
+ expect(this.bP).toBeUndefined()
expect(this.c).toBe(3)
+ expect(this.cP1).toBeUndefined()
},
mounted() {
calls.push('mixinB mounted')
}
}
- const Comp = {
- mixins: [mixinA, mixinB],
+ const mixinC = defineComponent({
+ props: ['cP1', 'cP2'],
data() {
return {
c: 3
}
},
- created(this: any) {
+ created() {
+ calls.push('mixinC created')
+ expect(this.c).toBe(3)
+ expect(this.cP1).toBeUndefined()
+ },
+ mounted() {
+ calls.push('mixinC mounted')
+ }
+ })
+ const Comp = defineComponent({
+ props: {
+ aaa: String
+ },
+ mixins: [defineComponent(mixinA), defineComponent(mixinB), mixinC],
+ data() {
+ return {
+ z: 4
+ }
+ },
+ created() {
calls.push('comp created')
expect(this.a).toBe(1)
expect(this.b).toBe(2)
+ expect(this.bP).toBeUndefined()
expect(this.c).toBe(3)
+ expect(this.cP2).toBeUndefined()
+ expect(this.z).toBe(4)
},
mounted() {
calls.push('comp mounted')
},
- render(this: any) {
+ render() {
return `${this.a}${this.b}${this.c}`
}
- }
-
+ })
expect(renderToString(h(Comp))).toBe(`123`)
expect(calls).toEqual([
'mixinA created',
'mixinB created',
+ 'mixinC created',
'comp created',
'mixinA mounted',
'mixinB mounted',
+ 'mixinC mounted',
'comp mounted'
])
})
a: 1
}
},
- mounted() {
+ methods: {
+ sayA() {}
+ },
+ mounted(this: any) {
+ expect(this.a).toBe(1)
+ expect(this.b).toBe(2)
calls.push('base')
}
}
- const Comp = {
- extends: Base,
+ const Comp = defineComponent({
+ extends: defineComponent(Base),
data() {
return {
b: 2
mounted() {
calls.push('comp')
},
- render(this: any) {
+ render() {
return `${this.a}${this.b}`
}
- }
+ })
expect(renderToString(h(Comp))).toBe(`12`)
expect(calls).toEqual(['base', 'comp'])
})
+ test('extends with mixins', () => {
+ const calls: string[] = []
+ const Base = {
+ data() {
+ return {
+ a: 1
+ }
+ },
+ methods: {
+ sayA() {}
+ },
+ mounted(this: any) {
+ expect(this.a).toBe(1)
+ expect(this.b).toBeTruthy()
+ expect(this.c).toBe(2)
+ calls.push('base')
+ }
+ }
+ const Base2 = {
+ data() {
+ return {
+ b: true
+ }
+ },
+ mounted(this: any) {
+ expect(this.a).toBe(1)
+ expect(this.b).toBeTruthy()
+ expect(this.c).toBe(2)
+ calls.push('base2')
+ }
+ }
+ const Comp = defineComponent({
+ extends: defineComponent(Base),
+ mixins: [defineComponent(Base2)],
+ data() {
+ return {
+ c: 2
+ }
+ },
+ mounted() {
+ calls.push('comp')
+ },
+ render() {
+ return `${this.a}${this.b}${this.c}`
+ }
+ })
+
+ expect(renderToString(h(Comp))).toBe(`1true2`)
+ expect(calls).toEqual(['base', 'base2', 'comp'])
+ })
+
test('accessing setup() state from options', async () => {
const Comp = defineComponent({
setup() {
ComponentOptionsWithoutProps,
ComponentOptionsWithArrayProps,
ComponentOptionsWithObjectProps,
+ ComponentOptionsMixin,
RenderFunction
} from './componentOptions'
import { SetupContext, FunctionalComponent } from './component'
-import { ComponentPublicInstance } from './componentProxy'
+import {
+ CreateComponentPublicInstance,
+ ComponentPublicInstanceConstructor
+} from './componentProxy'
import { ExtractPropTypes, ComponentPropsOptions } from './componentProps'
import { EmitsOptions } from './componentEmits'
import { isFunction } from '@vue/shared'
props: Readonly<Props>,
ctx: SetupContext
) => RawBindings | RenderFunction
-): {
- new (): ComponentPublicInstance<
+): ComponentPublicInstanceConstructor<
+ CreateComponentPublicInstance<
Props,
RawBindings,
{},
{},
{},
+ {},
+ {},
+ {},
// public props
VNodeProps & Props
>
-} & FunctionalComponent<Props>
+> &
+ FunctionalComponent<Props>
// overload 2: object format with no props
// (uses user defined props interface)
D = {},
C extends ComputedOptions = {},
M extends MethodOptions = {},
+ Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
+ Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
E extends EmitsOptions = Record<string, any>,
EE extends string = string
>(
- options: ComponentOptionsWithoutProps<Props, RawBindings, D, C, M, E, EE>
-): {
- new (): ComponentPublicInstance<
+ options: ComponentOptionsWithoutProps<
Props,
RawBindings,
D,
C,
M,
+ Mixin,
+ Extends,
+ E,
+ EE
+ >
+): ComponentPublicInstanceConstructor<
+ CreateComponentPublicInstance<
+ Props,
+ RawBindings,
+ D,
+ C,
+ M,
+ Mixin,
+ Extends,
E,
VNodeProps & Props
>
-} & ComponentOptionsWithoutProps<Props, RawBindings, D, C, M, E, EE>
+> &
+ ComponentOptionsWithoutProps<
+ Props,
+ RawBindings,
+ D,
+ C,
+ M,
+ Mixin,
+ Extends,
+ E,
+ EE
+ >
// overload 3: object format with array props declaration
// props inferred as { [key in PropNames]?: any }
D,
C extends ComputedOptions = {},
M extends MethodOptions = {},
+ Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
+ Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
E extends EmitsOptions = Record<string, any>,
EE extends string = string
>(
D,
C,
M,
+ Mixin,
+ Extends,
+ E,
+ EE
+ >
+): ComponentPublicInstanceConstructor<
+ // array props technically doesn't place any contraints on props in TSX before,
+ // but now we can export array props in TSX
+ CreateComponentPublicInstance<
+ Readonly<{ [key in PropNames]?: any }>,
+ RawBindings,
+ D,
+ C,
+ M,
+ Mixin,
+ Extends,
+ E
+ >
+> &
+ ComponentOptionsWithArrayProps<
+ PropNames,
+ RawBindings,
+ D,
+ C,
+ M,
+ Mixin,
+ Extends,
E,
EE
>
-): {
- // array props technically doesn't place any constraints on props in TSX
- new (): ComponentPublicInstance<VNodeProps, RawBindings, D, C, M, E>
-} & ComponentOptionsWithArrayProps<PropNames, RawBindings, D, C, M, E, EE>
// overload 4: object format with object props declaration
// see `ExtractPropTypes` in ./componentProps.ts
D,
C extends ComputedOptions = {},
M extends MethodOptions = {},
+ Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
+ Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
E extends EmitsOptions = Record<string, any>,
EE extends string = string
>(
D,
C,
M,
+ Mixin,
+ Extends,
E,
EE
>
-): {
- new (): ComponentPublicInstance<
+): ComponentPublicInstanceConstructor<
+ CreateComponentPublicInstance<
ExtractPropTypes<PropsOptions>,
RawBindings,
D,
C,
M,
+ Mixin,
+ Extends,
E,
VNodeProps & ExtractPropTypes<PropsOptions, false>
>
-} & ComponentOptionsWithObjectProps<PropsOptions, RawBindings, D, C, M, E, EE>
+> &
+ ComponentOptionsWithObjectProps<
+ PropsOptions,
+ RawBindings,
+ D,
+ C,
+ M,
+ Mixin,
+ Extends,
+ E,
+ EE
+ >
// implementation, close to no-op
export function defineComponent(options: unknown) {
shallowReadonly
} from '@vue/reactivity'
import {
+ CreateComponentPublicInstance,
ComponentPublicInstance,
PublicInstanceProxyHandlers,
RuntimeCompiledPublicInstanceProxyHandlers,
// The constructor type is an artificial type returned by defineComponent().
export type PublicAPIComponent =
| Component
- | { new (...args: any[]): ComponentPublicInstance<any, any, any, any, any> }
+ | {
+ new (...args: any[]): CreateComponentPublicInstance<
+ any,
+ any,
+ any,
+ any,
+ any
+ >
+ }
export { ComponentOptions }
import { callWithAsyncErrorHandling, ErrorCodes } from './errorHandling'
import { warn } from './warning'
import { normalizePropsOptions } from './componentProps'
+import { UnionToIntersection } from './helpers/typeUtils'
export type ObjectEmitsOptions = Record<
string,
>
export type EmitsOptions = ObjectEmitsOptions | string[]
-type UnionToIntersection<U> = (U extends any
- ? (k: U) => void
- : never) extends ((k: infer I) => void)
- ? I
- : never
-
export type EmitFn<
Options = ObjectEmitsOptions,
Event extends keyof Options = keyof Options
} from './componentProps'
import { EmitsOptions } from './componentEmits'
import { Directive } from './directives'
-import { ComponentPublicInstance } from './componentProxy'
+import {
+ CreateComponentPublicInstance,
+ ComponentPublicInstance
+} from './componentProxy'
import { warn } from './warning'
import { VNodeChild } from './vnode'
D,
C extends ComputedOptions,
M extends MethodOptions,
+ Mixin extends ComponentOptionsMixin,
+ Extends extends ComponentOptionsMixin,
E extends EmitsOptions,
EE extends string = string
>
- extends LegacyOptions<Props, D, C, M>,
+ extends LegacyOptions<Props, D, C, M, Mixin, Extends>,
SFCInternalOptions,
ComponentCustomOptions {
setup?: (
D = {},
C extends ComputedOptions = {},
M extends MethodOptions = {},
+ Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
+ Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
E extends EmitsOptions = EmitsOptions,
EE extends string = string
-> = ComponentOptionsBase<Props, RawBindings, D, C, M, E, EE> & {
+> = ComponentOptionsBase<Props, RawBindings, D, C, M, Mixin, Extends, E, EE> & {
props?: undefined
} & ThisType<
- ComponentPublicInstance<{}, RawBindings, D, C, M, E, Readonly<Props>>
+ CreateComponentPublicInstance<
+ {},
+ RawBindings,
+ D,
+ C,
+ M,
+ Mixin,
+ Extends,
+ E,
+ Readonly<Props>
+ >
>
export type ComponentOptionsWithArrayProps<
D = {},
C extends ComputedOptions = {},
M extends MethodOptions = {},
+ Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
+ Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
E extends EmitsOptions = EmitsOptions,
EE extends string = string,
Props = Readonly<{ [key in PropNames]?: any }>
-> = ComponentOptionsBase<Props, RawBindings, D, C, M, E, EE> & {
+> = ComponentOptionsBase<Props, RawBindings, D, C, M, Mixin, Extends, E, EE> & {
props: PropNames[]
-} & ThisType<ComponentPublicInstance<Props, RawBindings, D, C, M, E>>
+} & ThisType<
+ CreateComponentPublicInstance<
+ Props,
+ RawBindings,
+ D,
+ C,
+ M,
+ Mixin,
+ Extends,
+ E
+ >
+ >
export type ComponentOptionsWithObjectProps<
PropsOptions = ComponentObjectPropsOptions,
D = {},
C extends ComputedOptions = {},
M extends MethodOptions = {},
+ Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
+ Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
E extends EmitsOptions = EmitsOptions,
EE extends string = string,
Props = Readonly<ExtractPropTypes<PropsOptions>>
-> = ComponentOptionsBase<Props, RawBindings, D, C, M, E, EE> & {
+> = ComponentOptionsBase<Props, RawBindings, D, C, M, Mixin, Extends, E, EE> & {
props: PropsOptions
-} & ThisType<ComponentPublicInstance<Props, RawBindings, D, C, M, E>>
+} & ThisType<
+ CreateComponentPublicInstance<
+ Props,
+ RawBindings,
+ D,
+ C,
+ M,
+ Mixin,
+ Extends,
+ E
+ >
+ >
export type ComponentOptions =
| ComponentOptionsWithoutProps<any, any, any, any, any>
| ComponentOptionsWithObjectProps<any, any, any, any, any>
| ComponentOptionsWithArrayProps<any, any, any, any, any>
+export type ComponentOptionsMixin = ComponentOptionsBase<
+ any,
+ any,
+ any,
+ any,
+ any,
+ any,
+ any,
+ any,
+ any
+>
+
export type ComputedOptions = Record<
string,
ComputedGetter<any> | WritableComputedOptions<any>
Props,
D,
C extends ComputedOptions,
- M extends MethodOptions
+ M extends MethodOptions,
+ Mixin extends ComponentOptionsMixin,
+ Extends extends ComponentOptionsMixin
> {
// allow any custom options
[key: string]: any
// since that leads to some sort of circular inference and breaks ThisType
// for the entire component.
data?: (
- this: ComponentPublicInstance<Props>,
- vm: ComponentPublicInstance<Props>
+ this: CreateComponentPublicInstance<Props>,
+ vm: CreateComponentPublicInstance<Props>
) => D
computed?: C
methods?: M
inject?: ComponentInjectOptions
// composition
- mixins?: ComponentOptions[]
- extends?: ComponentOptions
+ mixins?: Mixin[]
+ extends?: Extends
// lifecycle
beforeCreate?(): void
errorCaptured?: ErrorCapturedHook
}
+export type OptionTypesKeys = 'P' | 'B' | 'D' | 'C' | 'M'
+
+export type OptionTypesType<
+ P = {},
+ B = {},
+ D = {},
+ C extends ComputedOptions = {},
+ M extends MethodOptions = {}
+> = {
+ P: P
+ B: B
+ D: D
+ C: C
+ M: M
+}
+
const enum OptionTypes {
PROPS = 'Props',
DATA = 'Data',
type InferPropType<T> = T extends null
? any // null & true would fail to infer
: T extends { type: null | true }
- ? any // somehow `ObjectConstructor` when inferred from { (): T } becomes `any`
+ ? any // As TS issue https://github.com/Microsoft/TypeScript/issues/14829 // somehow `ObjectConstructor` when inferred from { (): T } becomes `any` // `BooleanConstructor` when inferred from PropConstructor(with PropMethod) becomes `Boolean`
: T extends ObjectConstructor | { type: ObjectConstructor }
? { [key: string]: any }
- : T extends Prop<infer V> ? V : T
+ : T extends BooleanConstructor | { type: BooleanConstructor }
+ ? boolean
+ : T extends Prop<infer V> ? V : T
export type ExtractPropTypes<
O,
ComponentOptionsBase,
ComputedOptions,
MethodOptions,
+ ComponentOptionsMixin,
+ OptionTypesType,
+ OptionTypesKeys,
resolveMergedOptions
} from './componentOptions'
import { normalizePropsOptions } from './componentProps'
markAttrsAccessed
} from './componentRenderUtils'
import { warn } from './warning'
+import { UnionToIntersection } from './helpers/typeUtils'
/**
* Custom properties added to component instances in any way and can be accessed through `this`
*/
export interface ComponentCustomProperties {}
+type IsDefaultMixinComponent<T> = T extends ComponentOptionsMixin
+ ? ComponentOptionsMixin extends T ? true : false
+ : false
+
+type MixinToOptionTypes<T> = T extends ComponentOptionsBase<
+ infer P,
+ infer B,
+ infer D,
+ infer C,
+ infer M,
+ infer Mixin,
+ infer Extends,
+ any
+>
+ ? OptionTypesType<P & {}, B & {}, D & {}, C & {}, M & {}> &
+ IntersectionMixin<Mixin> &
+ IntersectionMixin<Extends>
+ : never
+
+// ExtractMixin(map type) is used to resolve circularly references
+type ExtractMixin<T> = {
+ Mixin: MixinToOptionTypes<T>
+}[T extends ComponentOptionsMixin ? 'Mixin' : never]
+
+type IntersectionMixin<T> = IsDefaultMixinComponent<T> extends true
+ ? OptionTypesType<{}, {}, {}, {}, {}>
+ : UnionToIntersection<ExtractMixin<T>>
+
+type UnwrapMixinsType<
+ T,
+ Type extends OptionTypesKeys
+> = T extends OptionTypesType ? T[Type] : never
+
+type EnsureNonVoid<T> = T extends void ? {} : T
+
+export type CreateComponentPublicInstance<
+ P = {},
+ B = {},
+ D = {},
+ C extends ComputedOptions = {},
+ M extends MethodOptions = {},
+ Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
+ Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
+ E extends EmitsOptions = {},
+ PublicProps = P,
+ PublicMixin = IntersectionMixin<Mixin> & IntersectionMixin<Extends>,
+ PublicP = UnwrapMixinsType<PublicMixin, 'P'> & EnsureNonVoid<P>,
+ PublicB = UnwrapMixinsType<PublicMixin, 'B'> & EnsureNonVoid<B>,
+ PublicD = UnwrapMixinsType<PublicMixin, 'D'> & EnsureNonVoid<D>,
+ PublicC extends ComputedOptions = UnwrapMixinsType<PublicMixin, 'C'> &
+ EnsureNonVoid<C>,
+ PublicM extends MethodOptions = UnwrapMixinsType<PublicMixin, 'M'> &
+ EnsureNonVoid<M>
+> = ComponentPublicInstance<
+ PublicP,
+ PublicB,
+ PublicD,
+ PublicC,
+ PublicM,
+ E,
+ PublicProps,
+ ComponentOptionsBase<P, B, D, C, M, Mixin, Extends, E>
+>
// public properties exposed on the proxy, which is used as the render context
// in templates (as `this` in the render option)
export type ComponentPublicInstance<
C extends ComputedOptions = {},
M extends MethodOptions = {},
E extends EmitsOptions = {},
- PublicProps = P
+ PublicProps = P,
+ Options = ComponentOptionsBase<any, any, any, any, any, any, any, any>
> = {
$: ComponentInternalInstance
$data: D
- $props: PublicProps
+ $props: P & PublicProps
$attrs: Data
$refs: Data
$slots: Slots
$parent: ComponentPublicInstance | null
$emit: EmitFn<E>
$el: any
- $options: ComponentOptionsBase<P, B, D, C, M, E>
+ $options: Options
$forceUpdate: ReactiveEffect
$nextTick: typeof nextTick
$watch: typeof instanceWatch
M &
ComponentCustomProperties
+export type ComponentPublicInstanceConstructor<
+ T extends ComponentPublicInstance
+> = {
+ new (): T
+}
+
const publicPropertiesMap: Record<
string,
(i: ComponentInternalInstance) => any
--- /dev/null
+export type UnionToIntersection<U> = (U extends any
+ ? (k: U) => void
+ : never) extends ((k: infer I) => void)
+ ? I
+ : never
ddd: string[]
eee: () => { a: string }
fff: (a: number, b: string) => { a: boolean }
+ hhh: boolean
}
type GT = string & { __brand: unknown }
fff: {
type: Function as PropType<(a: number, b: string) => { a: boolean }>,
required: true
+ },
+ hhh: {
+ type: Boolean,
+ required: true
}
},
setup(props) {
expectType<ExpectedProps['ddd']>(props.ddd)
expectType<ExpectedProps['eee']>(props.eee)
expectType<ExpectedProps['fff']>(props.fff)
+ expectType<ExpectedProps['hhh']>(props.hhh)
// @ts-expect-error props should be readonly
expectError((props.a = 1))
expectType<ExpectedProps['ddd']>(props.ddd)
expectType<ExpectedProps['eee']>(props.eee)
expectType<ExpectedProps['fff']>(props.fff)
+ expectType<ExpectedProps['hhh']>(props.hhh)
// @ts-expect-error props should be readonly
expectError((props.a = 1))
expectType<ExpectedProps['ddd']>(this.ddd)
expectType<ExpectedProps['eee']>(this.eee)
expectType<ExpectedProps['fff']>(this.fff)
+ expectType<ExpectedProps['hhh']>(this.hhh)
// @ts-expect-error props on `this` should be readonly
expectError((this.a = 1))
ddd={['ddd']}
eee={() => ({ a: 'eee' })}
fff={(a, b) => ({ a: a > +b })}
+ hhh={false}
// should allow extraneous as attrs
class="bar"
// should allow key
// })
describe('type inference w/ array props declaration', () => {
- defineComponent({
+ const MyComponent = defineComponent({
props: ['a', 'b'],
setup(props) {
// @ts-expect-error props should be readonly
expectType<number>(this.c)
}
})
+ expectType<JSX.Element>(<MyComponent a={[1, 2]} b="b" />)
+ // @ts-expect-error
+ expectError(<MyComponent other="other" />)
})
describe('type inference w/ options API', () => {
})
})
+describe('with mixins', () => {
+ const MixinA = defineComponent({
+ props: {
+ aP1: {
+ type: String,
+ default: 'aP1'
+ },
+ aP2: Boolean
+ },
+ data() {
+ return {
+ a: 1
+ }
+ }
+ })
+ const MixinB = defineComponent({
+ props: ['bP1', 'bP2'],
+ data() {
+ return {
+ b: 2
+ }
+ }
+ })
+ const MixinC = defineComponent({
+ data() {
+ return {
+ c: 3
+ }
+ }
+ })
+ const MixinD = defineComponent({
+ mixins: [MixinA],
+ data() {
+ return {
+ d: 4
+ }
+ },
+ computed: {
+ dC1(): number {
+ return this.d + this.a
+ },
+ dC2(): string {
+ return this.aP1 + 'dC2'
+ }
+ }
+ })
+ const MyComponent = defineComponent({
+ mixins: [MixinA, MixinB, MixinC, MixinD],
+ props: {
+ // required should make property non-void
+ z: {
+ type: String,
+ required: true
+ }
+ },
+ render() {
+ const props = this.$props
+ // props
+ expectType<string>(props.aP1)
+ expectType<boolean | undefined>(props.aP2)
+ expectType<any>(props.bP1)
+ expectType<any>(props.bP2)
+ expectType<string>(props.z)
+
+ const data = this.$data
+ expectType<number>(data.a)
+ expectType<number>(data.b)
+ expectType<number>(data.c)
+ expectType<number>(data.d)
+
+ // should also expose declared props on `this`
+ expectType<number>(this.a)
+ expectType<string>(this.aP1)
+ expectType<boolean | undefined>(this.aP2)
+ expectType<number>(this.b)
+ expectType<any>(this.bP1)
+ expectType<number>(this.c)
+ expectType<number>(this.d)
+ expectType<number>(this.dC1)
+ expectType<string>(this.dC2)
+
+ // props should be readonly
+ // @ts-expect-error
+ expectError((this.aP1 = 'new'))
+ // @ts-expect-error
+ expectError((this.z = 1))
+
+ // props on `this` should be readonly
+ // @ts-expect-error
+ expectError((this.bP1 = 1))
+
+ // string value can not assigned to number type value
+ // @ts-expect-error
+ expectError((this.c = '1'))
+
+ // setup context properties should be mutable
+ this.d = 5
+
+ return null
+ }
+ })
+
+ // Test TSX
+ expectType<JSX.Element>(
+ <MyComponent aP1={'aP'} aP2 bP1={1} bP2={[1, 2]} z={'z'} />
+ )
+
+ // missing required props
+ // @ts-expect-error
+ expectError(<MyComponent />)
+
+ // wrong prop types
+ // @ts-expect-error
+ expectError(<MyComponent aP1="ap" aP2={'wrong type'} bP1="b" z={'z'} />)
+ // @ts-expect-error
+ expectError(<MyComponent aP1={1} bP2={[1]} />)
+})
+
+describe('with extends', () => {
+ const Base = defineComponent({
+ props: {
+ aP1: Boolean,
+ aP2: {
+ type: Number,
+ default: 2
+ }
+ },
+ data() {
+ return {
+ a: 1
+ }
+ },
+ computed: {
+ c(): number {
+ return this.aP2 + this.a
+ }
+ }
+ })
+ const MyComponent = defineComponent({
+ extends: Base,
+ props: {
+ // required should make property non-void
+ z: {
+ type: String,
+ required: true
+ }
+ },
+ render() {
+ const props = this.$props
+ // props
+ expectType<boolean | undefined>(props.aP1)
+ expectType<number>(props.aP2)
+ expectType<string>(props.z)
+
+ const data = this.$data
+ expectType<number>(data.a)
+
+ // should also expose declared props on `this`
+ expectType<number>(this.a)
+ expectType<boolean | undefined>(this.aP1)
+ expectType<number>(this.aP2)
+
+ // setup context properties should be mutable
+ this.a = 5
+
+ return null
+ }
+ })
+
+ // Test TSX
+ expectType<JSX.Element>(<MyComponent aP2={3} aP1 z={'z'} />)
+
+ // missing required props
+ // @ts-expect-error
+ expectError(<MyComponent />)
+
+ // wrong prop types
+ // @ts-expect-error
+ expectError(<MyComponent aP2={'wrong type'} z={'z'} />)
+ // @ts-expect-error
+ expectError(<MyComponent aP1={3} />)
+})
+
+describe('extends with mixins', () => {
+ const Mixin = defineComponent({
+ props: {
+ mP1: {
+ type: String,
+ default: 'mP1'
+ },
+ mP2: Boolean
+ },
+ data() {
+ return {
+ a: 1
+ }
+ }
+ })
+ const Base = defineComponent({
+ props: {
+ p1: Boolean,
+ p2: {
+ type: Number,
+ default: 2
+ }
+ },
+ data() {
+ return {
+ b: 2
+ }
+ },
+ computed: {
+ c(): number {
+ return this.p2 + this.b
+ }
+ }
+ })
+ const MyComponent = defineComponent({
+ extends: Base,
+ mixins: [Mixin],
+ props: {
+ // required should make property non-void
+ z: {
+ type: String,
+ required: true
+ }
+ },
+ render() {
+ const props = this.$props
+ // props
+ expectType<boolean | undefined>(props.p1)
+ expectType<number>(props.p2)
+ expectType<string>(props.z)
+ expectType<string>(props.mP1)
+ expectType<boolean | undefined>(props.mP2)
+
+ const data = this.$data
+ expectType<number>(data.a)
+ expectType<number>(data.b)
+
+ // should also expose declared props on `this`
+ expectType<number>(this.a)
+ expectType<number>(this.b)
+ expectType<boolean | undefined>(this.p1)
+ expectType<number>(this.p2)
+ expectType<string>(this.mP1)
+ expectType<boolean | undefined>(this.mP2)
+
+ // setup context properties should be mutable
+ this.a = 5
+
+ return null
+ }
+ })
+
+ // Test TSX
+ expectType<JSX.Element>(<MyComponent mP1="p1" mP2 p1 p2={1} z={'z'} />)
+
+ // missing required props
+ // @ts-expect-error
+ expectError(<MyComponent />)
+
+ // wrong prop types
+ // @ts-expect-error
+ expectError(<MyComponent p2={'wrong type'} z={'z'} />)
+ // @ts-expect-error
+ expectError(<MyComponent mP1={3} />)
+})
+
describe('compatibility w/ createApp', () => {
const comp = defineComponent({})
createApp(comp).mount('#hello')