import { describe, Component, defineComponent, PropType, ref, Ref, expectError, expectType, ShallowUnwrapRef, FunctionalComponent, ComponentPublicInstance, toRefs, IsAny } from './index' declare function extractComponentOptions( obj: Component ): { props: Props rawBindings: RawBindings setup: ShallowUnwrapRef } describe('object props', () => { interface ExpectedProps { a?: number | undefined b: string e?: Function bb: string bbb: string cc?: string[] | undefined dd: { n: 1 } ee?: () => string ff?: (a: number, b: string) => { a: boolean } ccc?: string[] | undefined ddd: string[] eee: () => { a: string } fff: (a: number, b: string) => { a: boolean } hhh: boolean ggg: 'foo' | 'bar' ffff: (a: number, b: string) => { a: boolean } validated?: string object?: object } interface ExpectedRefs { a: Ref b: Ref e: Ref bb: Ref bbb: Ref cc: Ref dd: Ref<{ n: 1 }> ee: Ref<(() => string) | undefined> ff: Ref<((a: number, b: string) => { a: boolean }) | undefined> ccc: Ref ddd: Ref eee: Ref<() => { a: string }> fff: Ref<(a: number, b: string) => { a: boolean }> hhh: Ref ggg: Ref<'foo' | 'bar'> ffff: Ref<(a: number, b: string) => { a: boolean }> validated: Ref object: Ref zzz: any } describe('defineComponent', () => { const MyComponent = defineComponent({ props: { a: Number, // required should make property non-void b: { type: String, required: true }, e: Function, // default value should infer type and make it non-void bb: { default: 'hello' }, bbb: { // Note: default function value requires arrow syntax + explicit // annotation default: (props: any) => (props.bb as string) || 'foo' }, // explicit type casting cc: Array as PropType, // required + type casting dd: { type: Object as PropType<{ n: 1 }>, required: true }, // return type ee: Function as PropType<() => string>, // arguments + object return ff: Function as PropType<(a: number, b: string) => { a: boolean }>, // explicit type casting with constructor ccc: Array as () => string[], // required + constructor type casting ddd: { type: Array as () => string[], required: true }, // required + object return eee: { type: Function as PropType<() => { a: string }>, required: true }, // required + arguments + object return fff: { type: Function as PropType<(a: number, b: string) => { a: boolean }>, required: true }, hhh: { type: Boolean, required: true }, // default + type casting ggg: { type: String as PropType<'foo' | 'bar'>, default: 'foo' }, // default + function ffff: { type: Function as PropType<(a: number, b: string) => { a: boolean }>, default: (_a: number, _b: string) => ({ a: true }) }, validated: { type: String, // validator requires explicit annotation validator: (val: unknown) => val !== '' }, object: Object as PropType, zzz: Object as PropType }, setup(props) { const refs = toRefs(props) expectType(refs.a) expectType(refs.b) expectType(refs.e) expectType(refs.bb) expectType(refs.bbb) expectType(refs.cc) expectType(refs.dd) expectType(refs.ee) expectType(refs.ff) expectType(refs.ccc) expectType(refs.ddd) expectType(refs.eee) expectType(refs.fff) expectType(refs.hhh) expectType(refs.ggg) expectType(refs.ffff) expectType(refs.validated) expectType(refs.object) expectType>(true) return { setupA: 1, setupB: ref(1), setupC: { a: ref(2) }, setupD: undefined as Ref | undefined, setupProps: props } } }) const { props, rawBindings, setup } = extractComponentOptions(MyComponent) // props expectType(props.a) expectType(props.b) expectType(props.e) expectType(props.bb) expectType(props.bbb) expectType(props.cc) expectType(props.dd) expectType(props.ee) expectType(props.ff) expectType(props.ccc) expectType(props.ddd) expectType(props.eee) expectType(props.fff) expectType(props.hhh) expectType(props.ggg) expectType(props.ffff) expectType(props.validated) expectType(props.object) // raw bindings expectType(rawBindings.setupA) expectType>(rawBindings.setupB) expectType>(rawBindings.setupC.a) expectType | undefined>(rawBindings.setupD) // raw bindings props expectType(rawBindings.setupProps.a) expectType(rawBindings.setupProps.b) expectType(rawBindings.setupProps.e) expectType(rawBindings.setupProps.bb) expectType(rawBindings.setupProps.bbb) expectType(rawBindings.setupProps.cc) expectType(rawBindings.setupProps.dd) expectType(rawBindings.setupProps.ee) expectType(rawBindings.setupProps.ff) expectType(rawBindings.setupProps.ccc) expectType(rawBindings.setupProps.ddd) expectType(rawBindings.setupProps.eee) expectType(rawBindings.setupProps.fff) expectType(rawBindings.setupProps.hhh) expectType(rawBindings.setupProps.ggg) expectType(rawBindings.setupProps.ffff) expectType(rawBindings.setupProps.validated) // setup expectType(setup.setupA) expectType(setup.setupB) expectType>(setup.setupC.a) expectType(setup.setupD) // raw bindings props expectType(setup.setupProps.a) expectType(setup.setupProps.b) expectType(setup.setupProps.e) expectType(setup.setupProps.bb) expectType(setup.setupProps.bbb) expectType(setup.setupProps.cc) expectType(setup.setupProps.dd) expectType(setup.setupProps.ee) expectType(setup.setupProps.ff) expectType(setup.setupProps.ccc) expectType(setup.setupProps.ddd) expectType(setup.setupProps.eee) expectType(setup.setupProps.fff) expectType(setup.setupProps.hhh) expectType(setup.setupProps.ggg) expectType(setup.setupProps.ffff) expectType(setup.setupProps.validated) // instance const instance = new MyComponent() expectType(instance.setupA) expectType(instance.setupD) // @ts-expect-error instance.notExist }) describe('options', () => { const MyComponent = { props: { a: Number, // required should make property non-void b: { type: String, required: true }, e: Function, // default value should infer type and make it non-void bb: { default: 'hello' }, bbb: { // Note: default function value requires arrow syntax + explicit // annotation default: (props: any) => (props.bb as string) || 'foo' }, // explicit type casting cc: Array as PropType, // required + type casting dd: { type: Object as PropType<{ n: 1 }>, required: true }, // return type ee: Function as PropType<() => string>, // arguments + object return ff: Function as PropType<(a: number, b: string) => { a: boolean }>, // explicit type casting with constructor ccc: Array as () => string[], // required + constructor type casting ddd: { type: Array as () => string[], required: true }, // required + object return eee: { type: Function as PropType<() => { a: string }>, required: true }, // required + arguments + object return fff: { type: Function as PropType<(a: number, b: string) => { a: boolean }>, required: true }, hhh: { type: Boolean, required: true }, // default + type casting ggg: { type: String as PropType<'foo' | 'bar'>, default: 'foo' }, // default + function ffff: { type: Function as PropType<(a: number, b: string) => { a: boolean }>, default: (_a: number, _b: string) => ({ a: true }) }, validated: { type: String, // validator requires explicit annotation validator: (val: unknown) => val !== '' }, object: Object as PropType }, setup() { return { setupA: 1 } } } as const const { props, rawBindings, setup } = extractComponentOptions(MyComponent) // props expectType(props.a) expectType(props.b) expectType(props.e) expectType(props.bb) expectType(props.bbb) expectType(props.cc) expectType(props.dd) expectType(props.ee) expectType(props.ff) expectType(props.ccc) expectType(props.ddd) expectType(props.eee) expectType(props.fff) expectType(props.hhh) expectType(props.ggg) // expectType(props.ffff) // todo fix expectType(props.validated) expectType(props.object) // rawBindings expectType(rawBindings.setupA) //setup expectType(setup.setupA) }) }) describe('array props', () => { describe('defineComponent', () => { const MyComponent = defineComponent({ props: ['a', 'b'], setup() { return { c: 1 } } }) const { props, rawBindings, setup } = extractComponentOptions(MyComponent) // @ts-expect-error props should be readonly expectError((props.a = 1)) expectType(props.a) expectType(props.b) expectType(rawBindings.c) expectType(setup.c) }) describe('options', () => { const MyComponent = { props: ['a', 'b'] as const, setup() { return { c: 1 } } } const { props, rawBindings, setup } = extractComponentOptions(MyComponent) // @ts-expect-error props should be readonly expectError((props.a = 1)) // TODO infer the correct keys // expectType(props.a) // expectType(props.b) expectType(rawBindings.c) expectType(setup.c) }) }) describe('no props', () => { describe('defineComponent', () => { const MyComponent = defineComponent({ setup() { return { setupA: 1 } } }) const { rawBindings, setup } = extractComponentOptions(MyComponent) expectType(rawBindings.setupA) expectType(setup.setupA) // instance const instance = new MyComponent() expectType(instance.setupA) // @ts-expect-error instance.notExist }) describe('options', () => { const MyComponent = { setup() { return { setupA: 1 } } } const { rawBindings, setup } = extractComponentOptions(MyComponent) expectType(rawBindings.setupA) expectType(setup.setupA) }) }) describe('functional', () => { // TODO `props.foo` is `number|undefined` // describe('defineComponent', () => { // const MyComponent = defineComponent((props: { foo: number }) => {}) // const { props } = extractComponentOptions(MyComponent) // expectType(props.foo) // }) describe('function', () => { const MyComponent = (props: { foo: number }) => props.foo const { props } = extractComponentOptions(MyComponent) expectType(props.foo) }) describe('typed', () => { const MyComponent: FunctionalComponent<{ foo: number }> = (_, _2) => {} const { props } = extractComponentOptions(MyComponent) expectType(props.foo) }) }) declare type VueClass = { new (): ComponentPublicInstance } describe('class', () => { const MyComponent: VueClass<{ foo: number }> = {} as any const { props } = extractComponentOptions(MyComponent) expectType(props.foo) })