component.test-d.ts 12 KB

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