import { describe, Component, defineComponent, PropType, ref, Ref, expectError, expectType, ShallowUnwrapRef, FunctionalComponent, ComponentPublicInstance } 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 } 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 + contructor 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 !== '' } }, setup(props) { return { setupA: 1, setupB: ref(1), setupC: { a: ref(2) }, 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) // raw bindings expectType(rawBindings.setupA) expectType>(rawBindings.setupB) expectType>(rawBindings.setupC.a) expectType(rawBindings.setupA) // 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.setupA) // 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) }) 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 + contructor 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 !== '' } }, 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) // 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) }) 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) })