component.test-d.ts 15 KB

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