component.test-d.ts 15 KB

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