component.test-d.ts 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. import {
  2. describe,
  3. Component,
  4. defineComponent,
  5. PropType,
  6. ref,
  7. Ref,
  8. expectError,
  9. expectType,
  10. ShallowUnwrapRef,
  11. FunctionalComponent,
  12. ComponentPublicInstance
  13. } from './index'
  14. declare function extractComponentOptions<Props, RawBindings>(
  15. obj: Component<Props, RawBindings>
  16. ): {
  17. props: Props
  18. rawBindings: RawBindings
  19. setup: ShallowUnwrapRef<RawBindings>
  20. }
  21. describe('object props', () => {
  22. interface ExpectedProps {
  23. a?: number | undefined
  24. b: string
  25. e?: Function
  26. bb: string
  27. bbb: string
  28. cc?: string[] | undefined
  29. dd: { n: 1 }
  30. ee?: () => string
  31. ff?: (a: number, b: string) => { a: boolean }
  32. ccc?: string[] | undefined
  33. ddd: string[]
  34. eee: () => { a: string }
  35. fff: (a: number, b: string) => { a: boolean }
  36. hhh: boolean
  37. ggg: 'foo' | 'bar'
  38. ffff: (a: number, b: string) => { a: boolean }
  39. validated?: string
  40. object?: object
  41. }
  42. describe('defineComponent', () => {
  43. const MyComponent = defineComponent({
  44. props: {
  45. a: Number,
  46. // required should make property non-void
  47. b: {
  48. type: String,
  49. required: true
  50. },
  51. e: Function,
  52. // default value should infer type and make it non-void
  53. bb: {
  54. default: 'hello'
  55. },
  56. bbb: {
  57. // Note: default function value requires arrow syntax + explicit
  58. // annotation
  59. default: (props: any) => (props.bb as string) || 'foo'
  60. },
  61. // explicit type casting
  62. cc: Array as PropType<string[]>,
  63. // required + type casting
  64. dd: {
  65. type: Object as PropType<{ n: 1 }>,
  66. required: true
  67. },
  68. // return type
  69. ee: Function as PropType<() => string>,
  70. // arguments + object return
  71. ff: Function as PropType<(a: number, b: string) => { a: boolean }>,
  72. // explicit type casting with constructor
  73. ccc: Array as () => string[],
  74. // required + contructor type casting
  75. ddd: {
  76. type: Array as () => string[],
  77. required: true
  78. },
  79. // required + object return
  80. eee: {
  81. type: Function as PropType<() => { a: string }>,
  82. required: true
  83. },
  84. // required + arguments + object return
  85. fff: {
  86. type: Function as PropType<(a: number, b: string) => { a: boolean }>,
  87. required: true
  88. },
  89. hhh: {
  90. type: Boolean,
  91. required: true
  92. },
  93. // default + type casting
  94. ggg: {
  95. type: String as PropType<'foo' | 'bar'>,
  96. default: 'foo'
  97. },
  98. // default + function
  99. ffff: {
  100. type: Function as PropType<(a: number, b: string) => { a: boolean }>,
  101. default: (_a: number, _b: string) => ({ a: true })
  102. },
  103. validated: {
  104. type: String,
  105. // validator requires explicit annotation
  106. validator: (val: unknown) => val !== ''
  107. },
  108. object: Object as PropType<object>
  109. },
  110. setup(props) {
  111. return {
  112. setupA: 1,
  113. setupB: ref(1),
  114. setupC: {
  115. a: ref(2)
  116. },
  117. setupProps: props
  118. }
  119. }
  120. })
  121. const { props, rawBindings, setup } = extractComponentOptions(MyComponent)
  122. // props
  123. expectType<ExpectedProps['a']>(props.a)
  124. expectType<ExpectedProps['b']>(props.b)
  125. expectType<ExpectedProps['e']>(props.e)
  126. expectType<ExpectedProps['bb']>(props.bb)
  127. expectType<ExpectedProps['bbb']>(props.bbb)
  128. expectType<ExpectedProps['cc']>(props.cc)
  129. expectType<ExpectedProps['dd']>(props.dd)
  130. expectType<ExpectedProps['ee']>(props.ee)
  131. expectType<ExpectedProps['ff']>(props.ff)
  132. expectType<ExpectedProps['ccc']>(props.ccc)
  133. expectType<ExpectedProps['ddd']>(props.ddd)
  134. expectType<ExpectedProps['eee']>(props.eee)
  135. expectType<ExpectedProps['fff']>(props.fff)
  136. expectType<ExpectedProps['hhh']>(props.hhh)
  137. expectType<ExpectedProps['ggg']>(props.ggg)
  138. expectType<ExpectedProps['ffff']>(props.ffff)
  139. expectType<ExpectedProps['validated']>(props.validated)
  140. expectType<ExpectedProps['object']>(props.object)
  141. // raw bindings
  142. expectType<Number>(rawBindings.setupA)
  143. expectType<Ref<Number>>(rawBindings.setupB)
  144. expectType<Ref<Number>>(rawBindings.setupC.a)
  145. expectType<Number>(rawBindings.setupA)
  146. // raw bindings props
  147. expectType<ExpectedProps['a']>(rawBindings.setupProps.a)
  148. expectType<ExpectedProps['b']>(rawBindings.setupProps.b)
  149. expectType<ExpectedProps['e']>(rawBindings.setupProps.e)
  150. expectType<ExpectedProps['bb']>(rawBindings.setupProps.bb)
  151. expectType<ExpectedProps['bbb']>(rawBindings.setupProps.bbb)
  152. expectType<ExpectedProps['cc']>(rawBindings.setupProps.cc)
  153. expectType<ExpectedProps['dd']>(rawBindings.setupProps.dd)
  154. expectType<ExpectedProps['ee']>(rawBindings.setupProps.ee)
  155. expectType<ExpectedProps['ff']>(rawBindings.setupProps.ff)
  156. expectType<ExpectedProps['ccc']>(rawBindings.setupProps.ccc)
  157. expectType<ExpectedProps['ddd']>(rawBindings.setupProps.ddd)
  158. expectType<ExpectedProps['eee']>(rawBindings.setupProps.eee)
  159. expectType<ExpectedProps['fff']>(rawBindings.setupProps.fff)
  160. expectType<ExpectedProps['hhh']>(rawBindings.setupProps.hhh)
  161. expectType<ExpectedProps['ggg']>(rawBindings.setupProps.ggg)
  162. expectType<ExpectedProps['ffff']>(rawBindings.setupProps.ffff)
  163. expectType<ExpectedProps['validated']>(rawBindings.setupProps.validated)
  164. // setup
  165. expectType<Number>(setup.setupA)
  166. expectType<Number>(setup.setupB)
  167. expectType<Ref<Number>>(setup.setupC.a)
  168. expectType<Number>(setup.setupA)
  169. // raw bindings props
  170. expectType<ExpectedProps['a']>(setup.setupProps.a)
  171. expectType<ExpectedProps['b']>(setup.setupProps.b)
  172. expectType<ExpectedProps['e']>(setup.setupProps.e)
  173. expectType<ExpectedProps['bb']>(setup.setupProps.bb)
  174. expectType<ExpectedProps['bbb']>(setup.setupProps.bbb)
  175. expectType<ExpectedProps['cc']>(setup.setupProps.cc)
  176. expectType<ExpectedProps['dd']>(setup.setupProps.dd)
  177. expectType<ExpectedProps['ee']>(setup.setupProps.ee)
  178. expectType<ExpectedProps['ff']>(setup.setupProps.ff)
  179. expectType<ExpectedProps['ccc']>(setup.setupProps.ccc)
  180. expectType<ExpectedProps['ddd']>(setup.setupProps.ddd)
  181. expectType<ExpectedProps['eee']>(setup.setupProps.eee)
  182. expectType<ExpectedProps['fff']>(setup.setupProps.fff)
  183. expectType<ExpectedProps['hhh']>(setup.setupProps.hhh)
  184. expectType<ExpectedProps['ggg']>(setup.setupProps.ggg)
  185. expectType<ExpectedProps['ffff']>(setup.setupProps.ffff)
  186. expectType<ExpectedProps['validated']>(setup.setupProps.validated)
  187. // instance
  188. const instance = new MyComponent()
  189. expectType<number>(instance.setupA)
  190. // @ts-expect-error
  191. instance.notExist
  192. })
  193. describe('options', () => {
  194. const MyComponent = {
  195. props: {
  196. a: Number,
  197. // required should make property non-void
  198. b: {
  199. type: String,
  200. required: true
  201. },
  202. e: Function,
  203. // default value should infer type and make it non-void
  204. bb: {
  205. default: 'hello'
  206. },
  207. bbb: {
  208. // Note: default function value requires arrow syntax + explicit
  209. // annotation
  210. default: (props: any) => (props.bb as string) || 'foo'
  211. },
  212. // explicit type casting
  213. cc: Array as PropType<string[]>,
  214. // required + type casting
  215. dd: {
  216. type: Object as PropType<{ n: 1 }>,
  217. required: true
  218. },
  219. // return type
  220. ee: Function as PropType<() => string>,
  221. // arguments + object return
  222. ff: Function as PropType<(a: number, b: string) => { a: boolean }>,
  223. // explicit type casting with constructor
  224. ccc: Array as () => string[],
  225. // required + contructor type casting
  226. ddd: {
  227. type: Array as () => string[],
  228. required: true
  229. },
  230. // required + object return
  231. eee: {
  232. type: Function as PropType<() => { a: string }>,
  233. required: true
  234. },
  235. // required + arguments + object return
  236. fff: {
  237. type: Function as PropType<(a: number, b: string) => { a: boolean }>,
  238. required: true
  239. },
  240. hhh: {
  241. type: Boolean,
  242. required: true
  243. },
  244. // default + type casting
  245. ggg: {
  246. type: String as PropType<'foo' | 'bar'>,
  247. default: 'foo'
  248. },
  249. // default + function
  250. ffff: {
  251. type: Function as PropType<(a: number, b: string) => { a: boolean }>,
  252. default: (_a: number, _b: string) => ({ a: true })
  253. },
  254. validated: {
  255. type: String,
  256. // validator requires explicit annotation
  257. validator: (val: unknown) => val !== ''
  258. },
  259. object: Object as PropType<object>
  260. },
  261. setup() {
  262. return {
  263. setupA: 1
  264. }
  265. }
  266. } as const
  267. const { props, rawBindings, setup } = extractComponentOptions(MyComponent)
  268. // props
  269. expectType<ExpectedProps['a']>(props.a)
  270. expectType<ExpectedProps['b']>(props.b)
  271. expectType<ExpectedProps['e']>(props.e)
  272. expectType<ExpectedProps['bb']>(props.bb)
  273. expectType<ExpectedProps['bbb']>(props.bbb)
  274. expectType<ExpectedProps['cc']>(props.cc)
  275. expectType<ExpectedProps['dd']>(props.dd)
  276. expectType<ExpectedProps['ee']>(props.ee)
  277. expectType<ExpectedProps['ff']>(props.ff)
  278. expectType<ExpectedProps['ccc']>(props.ccc)
  279. expectType<ExpectedProps['ddd']>(props.ddd)
  280. expectType<ExpectedProps['eee']>(props.eee)
  281. expectType<ExpectedProps['fff']>(props.fff)
  282. expectType<ExpectedProps['hhh']>(props.hhh)
  283. expectType<ExpectedProps['ggg']>(props.ggg)
  284. // expectType<ExpectedProps['ffff']>(props.ffff) // todo fix
  285. expectType<ExpectedProps['validated']>(props.validated)
  286. expectType<ExpectedProps['object']>(props.object)
  287. // rawBindings
  288. expectType<Number>(rawBindings.setupA)
  289. //setup
  290. expectType<Number>(setup.setupA)
  291. })
  292. })
  293. describe('array props', () => {
  294. describe('defineComponent', () => {
  295. const MyComponent = defineComponent({
  296. props: ['a', 'b'],
  297. setup() {
  298. return {
  299. c: 1
  300. }
  301. }
  302. })
  303. const { props, rawBindings, setup } = extractComponentOptions(MyComponent)
  304. // @ts-expect-error props should be readonly
  305. expectError((props.a = 1))
  306. expectType<any>(props.a)
  307. expectType<any>(props.b)
  308. expectType<number>(rawBindings.c)
  309. expectType<number>(setup.c)
  310. })
  311. describe('options', () => {
  312. const MyComponent = {
  313. props: ['a', 'b'] as const,
  314. setup() {
  315. return {
  316. c: 1
  317. }
  318. }
  319. }
  320. const { props, rawBindings, setup } = extractComponentOptions(MyComponent)
  321. // @ts-expect-error props should be readonly
  322. expectError((props.a = 1))
  323. // TODO infer the correct keys
  324. // expectType<any>(props.a)
  325. // expectType<any>(props.b)
  326. expectType<number>(rawBindings.c)
  327. expectType<number>(setup.c)
  328. })
  329. })
  330. describe('no props', () => {
  331. describe('defineComponent', () => {
  332. const MyComponent = defineComponent({
  333. setup() {
  334. return {
  335. setupA: 1
  336. }
  337. }
  338. })
  339. const { rawBindings, setup } = extractComponentOptions(MyComponent)
  340. expectType<number>(rawBindings.setupA)
  341. expectType<number>(setup.setupA)
  342. // instance
  343. const instance = new MyComponent()
  344. expectType<number>(instance.setupA)
  345. // @ts-expect-error
  346. instance.notExist
  347. })
  348. describe('options', () => {
  349. const MyComponent = {
  350. setup() {
  351. return {
  352. setupA: 1
  353. }
  354. }
  355. }
  356. const { rawBindings, setup } = extractComponentOptions(MyComponent)
  357. expectType<number>(rawBindings.setupA)
  358. expectType<number>(setup.setupA)
  359. })
  360. })
  361. describe('functional', () => {
  362. // TODO `props.foo` is `number|undefined`
  363. // describe('defineComponent', () => {
  364. // const MyComponent = defineComponent((props: { foo: number }) => {})
  365. // const { props } = extractComponentOptions(MyComponent)
  366. // expectType<number>(props.foo)
  367. // })
  368. describe('function', () => {
  369. const MyComponent = (props: { foo: number }) => props.foo
  370. const { props } = extractComponentOptions(MyComponent)
  371. expectType<number>(props.foo)
  372. })
  373. describe('typed', () => {
  374. const MyComponent: FunctionalComponent<{ foo: number }> = (_, _2) => {}
  375. const { props } = extractComponentOptions(MyComponent)
  376. expectType<number>(props.foo)
  377. })
  378. })
  379. declare type VueClass<Props = {}> = {
  380. new (): ComponentPublicInstance<Props>
  381. }
  382. describe('class', () => {
  383. const MyComponent: VueClass<{ foo: number }> = {} as any
  384. const { props } = extractComponentOptions(MyComponent)
  385. expectType<number>(props.foo)
  386. })