defineComponent.test-d.tsx 25 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024
  1. import {
  2. describe,
  3. Component,
  4. defineComponent,
  5. PropType,
  6. ref,
  7. reactive,
  8. createApp,
  9. expectError,
  10. expectType,
  11. ComponentPublicInstance,
  12. ComponentOptions,
  13. SetupContext,
  14. IsUnion,
  15. h
  16. } from './index'
  17. describe('with object props', () => {
  18. interface ExpectedProps {
  19. a?: number | undefined
  20. b: string
  21. e?: Function
  22. h: boolean
  23. bb: string
  24. bbb: string
  25. cc?: string[] | undefined
  26. dd: { n: 1 }
  27. ee?: () => string
  28. ff?: (a: number, b: string) => { a: boolean }
  29. ccc?: string[] | undefined
  30. ddd: string[]
  31. eee: () => { a: string }
  32. fff: (a: number, b: string) => { a: boolean }
  33. hhh: boolean
  34. ggg: 'foo' | 'bar'
  35. ffff: (a: number, b: string) => { a: boolean }
  36. iii?: (() => string) | (() => number)
  37. jjj: ((arg1: string) => string) | ((arg1: string, arg2: string) => string)
  38. kkk?: any
  39. validated?: string
  40. date?: Date
  41. }
  42. type GT = string & { __brand: unknown }
  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. h: Boolean,
  53. // default value should infer type and make it non-void
  54. bb: {
  55. default: 'hello'
  56. },
  57. bbb: {
  58. // Note: default function value requires arrow syntax + explicit
  59. // annotation
  60. default: (props: any) => (props.bb as string) || 'foo'
  61. },
  62. // explicit type casting
  63. cc: Array as PropType<string[]>,
  64. // required + type casting
  65. dd: {
  66. type: Object as PropType<{ n: 1 }>,
  67. required: true
  68. },
  69. // return type
  70. ee: Function as PropType<() => string>,
  71. // arguments + object return
  72. ff: Function as PropType<(a: number, b: string) => { a: boolean }>,
  73. // explicit type casting with constructor
  74. ccc: Array as () => string[],
  75. // required + contructor type casting
  76. ddd: {
  77. type: Array as () => string[],
  78. required: true
  79. },
  80. // required + object return
  81. eee: {
  82. type: Function as PropType<() => { a: string }>,
  83. required: true
  84. },
  85. // required + arguments + object return
  86. fff: {
  87. type: Function as PropType<(a: number, b: string) => { a: boolean }>,
  88. required: true
  89. },
  90. hhh: {
  91. type: Boolean,
  92. required: true
  93. },
  94. // default + type casting
  95. ggg: {
  96. type: String as PropType<'foo' | 'bar'>,
  97. default: 'foo'
  98. },
  99. // default + function
  100. ffff: {
  101. type: Function as PropType<(a: number, b: string) => { a: boolean }>,
  102. default: (a: number, b: string) => ({ a: a > +b })
  103. },
  104. // union + function with different return types
  105. iii: Function as PropType<(() => string) | (() => number)>,
  106. // union + function with different args & same return type
  107. jjj: {
  108. type: Function as PropType<
  109. ((arg1: string) => string) | ((arg1: string, arg2: string) => string)
  110. >,
  111. required: true
  112. },
  113. kkk: null,
  114. validated: {
  115. type: String,
  116. // validator requires explicit annotation
  117. validator: (val: unknown) => val !== ''
  118. },
  119. date: Date
  120. },
  121. setup(props) {
  122. // type assertion. See https://github.com/SamVerschueren/tsd
  123. expectType<ExpectedProps['a']>(props.a)
  124. expectType<ExpectedProps['b']>(props.b)
  125. expectType<ExpectedProps['e']>(props.e)
  126. expectType<ExpectedProps['h']>(props.h)
  127. expectType<ExpectedProps['bb']>(props.bb)
  128. expectType<ExpectedProps['bbb']>(props.bbb)
  129. expectType<ExpectedProps['cc']>(props.cc)
  130. expectType<ExpectedProps['dd']>(props.dd)
  131. expectType<ExpectedProps['ee']>(props.ee)
  132. expectType<ExpectedProps['ff']>(props.ff)
  133. expectType<ExpectedProps['ccc']>(props.ccc)
  134. expectType<ExpectedProps['ddd']>(props.ddd)
  135. expectType<ExpectedProps['eee']>(props.eee)
  136. expectType<ExpectedProps['fff']>(props.fff)
  137. expectType<ExpectedProps['hhh']>(props.hhh)
  138. expectType<ExpectedProps['ggg']>(props.ggg)
  139. expectType<ExpectedProps['ffff']>(props.ffff)
  140. if (typeof props.iii !== 'function') {
  141. expectType<undefined>(props.iii)
  142. }
  143. expectType<ExpectedProps['iii']>(props.iii)
  144. expectType<IsUnion<typeof props.jjj>>(true)
  145. expectType<ExpectedProps['jjj']>(props.jjj)
  146. expectType<ExpectedProps['kkk']>(props.kkk)
  147. expectType<ExpectedProps['validated']>(props.validated)
  148. expectType<ExpectedProps['date']>(props.date)
  149. // @ts-expect-error props should be readonly
  150. expectError((props.a = 1))
  151. // setup context
  152. return {
  153. c: ref(1),
  154. d: {
  155. e: ref('hi')
  156. },
  157. f: reactive({
  158. g: ref('hello' as GT)
  159. })
  160. }
  161. },
  162. render() {
  163. const props = this.$props
  164. expectType<ExpectedProps['a']>(props.a)
  165. expectType<ExpectedProps['b']>(props.b)
  166. expectType<ExpectedProps['e']>(props.e)
  167. expectType<ExpectedProps['h']>(props.h)
  168. expectType<ExpectedProps['bb']>(props.bb)
  169. expectType<ExpectedProps['cc']>(props.cc)
  170. expectType<ExpectedProps['dd']>(props.dd)
  171. expectType<ExpectedProps['ee']>(props.ee)
  172. expectType<ExpectedProps['ff']>(props.ff)
  173. expectType<ExpectedProps['ccc']>(props.ccc)
  174. expectType<ExpectedProps['ddd']>(props.ddd)
  175. expectType<ExpectedProps['eee']>(props.eee)
  176. expectType<ExpectedProps['fff']>(props.fff)
  177. expectType<ExpectedProps['hhh']>(props.hhh)
  178. expectType<ExpectedProps['ggg']>(props.ggg)
  179. if (typeof props.iii !== 'function') {
  180. expectType<undefined>(props.iii)
  181. }
  182. expectType<ExpectedProps['iii']>(props.iii)
  183. expectType<IsUnion<typeof props.jjj>>(true)
  184. expectType<ExpectedProps['jjj']>(props.jjj)
  185. expectType<ExpectedProps['kkk']>(props.kkk)
  186. // @ts-expect-error props should be readonly
  187. expectError((props.a = 1))
  188. // should also expose declared props on `this`
  189. expectType<ExpectedProps['a']>(this.a)
  190. expectType<ExpectedProps['b']>(this.b)
  191. expectType<ExpectedProps['e']>(this.e)
  192. expectType<ExpectedProps['h']>(this.h)
  193. expectType<ExpectedProps['bb']>(this.bb)
  194. expectType<ExpectedProps['cc']>(this.cc)
  195. expectType<ExpectedProps['dd']>(this.dd)
  196. expectType<ExpectedProps['ee']>(this.ee)
  197. expectType<ExpectedProps['ff']>(this.ff)
  198. expectType<ExpectedProps['ccc']>(this.ccc)
  199. expectType<ExpectedProps['ddd']>(this.ddd)
  200. expectType<ExpectedProps['eee']>(this.eee)
  201. expectType<ExpectedProps['fff']>(this.fff)
  202. expectType<ExpectedProps['hhh']>(this.hhh)
  203. expectType<ExpectedProps['ggg']>(this.ggg)
  204. if (typeof this.iii !== 'function') {
  205. expectType<undefined>(this.iii)
  206. }
  207. expectType<ExpectedProps['iii']>(this.iii)
  208. const { jjj } = this
  209. expectType<IsUnion<typeof jjj>>(true)
  210. expectType<ExpectedProps['jjj']>(this.jjj)
  211. expectType<ExpectedProps['kkk']>(this.kkk)
  212. // @ts-expect-error props on `this` should be readonly
  213. expectError((this.a = 1))
  214. // assert setup context unwrapping
  215. expectType<number>(this.c)
  216. expectType<string>(this.d.e.value)
  217. expectType<GT>(this.f.g)
  218. // setup context properties should be mutable
  219. this.c = 2
  220. return null
  221. }
  222. })
  223. expectType<Component>(MyComponent)
  224. // Test TSX
  225. expectType<JSX.Element>(
  226. <MyComponent
  227. a={1}
  228. b="b"
  229. bb="bb"
  230. e={() => {}}
  231. cc={['cc']}
  232. dd={{ n: 1 }}
  233. ee={() => 'ee'}
  234. ccc={['ccc']}
  235. ddd={['ddd']}
  236. eee={() => ({ a: 'eee' })}
  237. fff={(a, b) => ({ a: a > +b })}
  238. hhh={false}
  239. ggg="foo"
  240. jjj={() => ''}
  241. // should allow class/style as attrs
  242. class="bar"
  243. style={{ color: 'red' }}
  244. // should allow key
  245. key={'foo'}
  246. // should allow ref
  247. ref={'foo'}
  248. />
  249. )
  250. expectType<Component>(
  251. <MyComponent
  252. b="b"
  253. dd={{ n: 1 }}
  254. ddd={['ddd']}
  255. eee={() => ({ a: 'eee' })}
  256. fff={(a, b) => ({ a: a > +b })}
  257. hhh={false}
  258. jjj={() => ''}
  259. />
  260. )
  261. // @ts-expect-error missing required props
  262. expectError(<MyComponent />)
  263. expectError(
  264. // @ts-expect-error wrong prop types
  265. <MyComponent a={'wrong type'} b="foo" dd={{ n: 1 }} ddd={['foo']} />
  266. )
  267. expectError(
  268. // @ts-expect-error wrong prop types
  269. <MyComponent ggg="baz" />
  270. )
  271. // @ts-expect-error
  272. expectError(<MyComponent b="foo" dd={{ n: 'string' }} ddd={['foo']} />)
  273. // `this` should be void inside of prop validators and prop default factories
  274. defineComponent({
  275. props: {
  276. myProp: {
  277. type: Number,
  278. validator(val: unknown): boolean {
  279. // @ts-expect-error
  280. return val !== this.otherProp
  281. },
  282. default(): number {
  283. // @ts-expect-error
  284. return this.otherProp + 1
  285. }
  286. },
  287. otherProp: {
  288. type: Number,
  289. required: true
  290. }
  291. }
  292. })
  293. })
  294. // describe('type inference w/ optional props declaration', () => {
  295. // const MyComponent = defineComponent({
  296. // setup(_props: { msg: string }) {
  297. // return {
  298. // a: 1
  299. // }
  300. // },
  301. // render() {
  302. // expectType<string>(this.$props.msg)
  303. // // props should be readonly
  304. // expectError((this.$props.msg = 'foo'))
  305. // // should not expose on `this`
  306. // expectError(this.msg)
  307. // expectType<number>(this.a)
  308. // return null
  309. // }
  310. // })
  311. // expectType<JSX.Element>(<MyComponent msg="foo" />)
  312. // expectError(<MyComponent />)
  313. // expectError(<MyComponent msg={1} />)
  314. // })
  315. // describe('type inference w/ direct setup function', () => {
  316. // const MyComponent = defineComponent((_props: { msg: string }) => {})
  317. // expectType<JSX.Element>(<MyComponent msg="foo" />)
  318. // expectError(<MyComponent />)
  319. // expectError(<MyComponent msg={1} />)
  320. // })
  321. describe('type inference w/ array props declaration', () => {
  322. const MyComponent = defineComponent({
  323. props: ['a', 'b'],
  324. setup(props) {
  325. // @ts-expect-error props should be readonly
  326. expectError((props.a = 1))
  327. expectType<any>(props.a)
  328. expectType<any>(props.b)
  329. return {
  330. c: 1
  331. }
  332. },
  333. render() {
  334. expectType<any>(this.$props.a)
  335. expectType<any>(this.$props.b)
  336. // @ts-expect-error
  337. expectError((this.$props.a = 1))
  338. expectType<any>(this.a)
  339. expectType<any>(this.b)
  340. expectType<number>(this.c)
  341. }
  342. })
  343. expectType<JSX.Element>(<MyComponent a={[1, 2]} b="b" />)
  344. // @ts-expect-error
  345. expectError(<MyComponent other="other" />)
  346. })
  347. describe('type inference w/ options API', () => {
  348. defineComponent({
  349. props: { a: Number },
  350. setup() {
  351. return {
  352. b: 123
  353. }
  354. },
  355. data() {
  356. // Limitation: we cannot expose the return result of setup() on `this`
  357. // here in data() - somehow that would mess up the inference
  358. expectType<number | undefined>(this.a)
  359. return {
  360. c: this.a || 123,
  361. someRef: ref(0)
  362. }
  363. },
  364. computed: {
  365. d(): number {
  366. expectType<number>(this.b)
  367. return this.b + 1
  368. },
  369. e: {
  370. get(): number {
  371. expectType<number>(this.b)
  372. expectType<number>(this.d)
  373. return this.b + this.d
  374. },
  375. set(v: number) {
  376. expectType<number>(this.b)
  377. expectType<number>(this.d)
  378. expectType<number>(v)
  379. }
  380. }
  381. },
  382. watch: {
  383. a() {
  384. expectType<number>(this.b)
  385. this.b + 1
  386. }
  387. },
  388. created() {
  389. // props
  390. expectType<number | undefined>(this.a)
  391. // returned from setup()
  392. expectType<number>(this.b)
  393. // returned from data()
  394. expectType<number>(this.c)
  395. // computed
  396. expectType<number>(this.d)
  397. // computed get/set
  398. expectType<number>(this.e)
  399. expectType<number>(this.someRef)
  400. },
  401. methods: {
  402. doSomething() {
  403. // props
  404. expectType<number | undefined>(this.a)
  405. // returned from setup()
  406. expectType<number>(this.b)
  407. // returned from data()
  408. expectType<number>(this.c)
  409. // computed
  410. expectType<number>(this.d)
  411. // computed get/set
  412. expectType<number>(this.e)
  413. },
  414. returnSomething() {
  415. return this.a
  416. }
  417. },
  418. render() {
  419. // props
  420. expectType<number | undefined>(this.a)
  421. // returned from setup()
  422. expectType<number>(this.b)
  423. // returned from data()
  424. expectType<number>(this.c)
  425. // computed
  426. expectType<number>(this.d)
  427. // computed get/set
  428. expectType<number>(this.e)
  429. // method
  430. expectType<() => number | undefined>(this.returnSomething)
  431. }
  432. })
  433. })
  434. describe('with mixins', () => {
  435. const MixinA = defineComponent({
  436. props: {
  437. aP1: {
  438. type: String,
  439. default: 'aP1'
  440. },
  441. aP2: Boolean
  442. },
  443. data() {
  444. return {
  445. a: 1
  446. }
  447. }
  448. })
  449. const MixinB = defineComponent({
  450. props: ['bP1', 'bP2'],
  451. data() {
  452. return {
  453. b: 2
  454. }
  455. }
  456. })
  457. const MixinC = defineComponent({
  458. data() {
  459. return {
  460. c: 3
  461. }
  462. }
  463. })
  464. const MixinD = defineComponent({
  465. mixins: [MixinA],
  466. data() {
  467. //@ts-expect-error computed are not available on data()
  468. expectError<number>(this.dC1)
  469. //@ts-expect-error computed are not available on data()
  470. expectError<string>(this.dC2)
  471. return {
  472. d: 4
  473. }
  474. },
  475. setup(props) {
  476. expectType<string>(props.aP1)
  477. },
  478. computed: {
  479. dC1(): number {
  480. return this.d + this.a
  481. },
  482. dC2(): string {
  483. return this.aP1 + 'dC2'
  484. }
  485. }
  486. })
  487. const MyComponent = defineComponent({
  488. mixins: [MixinA, MixinB, MixinC, MixinD],
  489. props: {
  490. // required should make property non-void
  491. z: {
  492. type: String,
  493. required: true
  494. }
  495. },
  496. data(vm) {
  497. expectType<number>(vm.a)
  498. expectType<number>(vm.b)
  499. expectType<number>(vm.c)
  500. expectType<number>(vm.d)
  501. // should also expose declared props on `this`
  502. expectType<number>(this.a)
  503. expectType<string>(this.aP1)
  504. expectType<boolean | undefined>(this.aP2)
  505. expectType<number>(this.b)
  506. expectType<any>(this.bP1)
  507. expectType<number>(this.c)
  508. expectType<number>(this.d)
  509. return {}
  510. },
  511. setup(props) {
  512. expectType<string>(props.z)
  513. // props
  514. expectType<string>(props.aP1)
  515. expectType<boolean | undefined>(props.aP2)
  516. expectType<any>(props.bP1)
  517. expectType<any>(props.bP2)
  518. expectType<string>(props.z)
  519. },
  520. render() {
  521. const props = this.$props
  522. // props
  523. expectType<string>(props.aP1)
  524. expectType<boolean | undefined>(props.aP2)
  525. expectType<any>(props.bP1)
  526. expectType<any>(props.bP2)
  527. expectType<string>(props.z)
  528. const data = this.$data
  529. expectType<number>(data.a)
  530. expectType<number>(data.b)
  531. expectType<number>(data.c)
  532. expectType<number>(data.d)
  533. // should also expose declared props on `this`
  534. expectType<number>(this.a)
  535. expectType<string>(this.aP1)
  536. expectType<boolean | undefined>(this.aP2)
  537. expectType<number>(this.b)
  538. expectType<any>(this.bP1)
  539. expectType<number>(this.c)
  540. expectType<number>(this.d)
  541. expectType<number>(this.dC1)
  542. expectType<string>(this.dC2)
  543. // props should be readonly
  544. // @ts-expect-error
  545. expectError((this.aP1 = 'new'))
  546. // @ts-expect-error
  547. expectError((this.z = 1))
  548. // props on `this` should be readonly
  549. // @ts-expect-error
  550. expectError((this.bP1 = 1))
  551. // string value can not assigned to number type value
  552. // @ts-expect-error
  553. expectError((this.c = '1'))
  554. // setup context properties should be mutable
  555. this.d = 5
  556. return null
  557. }
  558. })
  559. // Test TSX
  560. expectType<JSX.Element>(
  561. <MyComponent aP1={'aP'} aP2 bP1={1} bP2={[1, 2]} z={'z'} />
  562. )
  563. // missing required props
  564. // @ts-expect-error
  565. expectError(<MyComponent />)
  566. // wrong prop types
  567. // @ts-expect-error
  568. expectError(<MyComponent aP1="ap" aP2={'wrong type'} bP1="b" z={'z'} />)
  569. // @ts-expect-error
  570. expectError(<MyComponent aP1={1} bP2={[1]} />)
  571. })
  572. describe('with extends', () => {
  573. const Base = defineComponent({
  574. props: {
  575. aP1: Boolean,
  576. aP2: {
  577. type: Number,
  578. default: 2
  579. }
  580. },
  581. data() {
  582. return {
  583. a: 1
  584. }
  585. },
  586. computed: {
  587. c(): number {
  588. return this.aP2 + this.a
  589. }
  590. }
  591. })
  592. const MyComponent = defineComponent({
  593. extends: Base,
  594. props: {
  595. // required should make property non-void
  596. z: {
  597. type: String,
  598. required: true
  599. }
  600. },
  601. render() {
  602. const props = this.$props
  603. // props
  604. expectType<boolean | undefined>(props.aP1)
  605. expectType<number>(props.aP2)
  606. expectType<string>(props.z)
  607. const data = this.$data
  608. expectType<number>(data.a)
  609. // should also expose declared props on `this`
  610. expectType<number>(this.a)
  611. expectType<boolean | undefined>(this.aP1)
  612. expectType<number>(this.aP2)
  613. // setup context properties should be mutable
  614. this.a = 5
  615. return null
  616. }
  617. })
  618. // Test TSX
  619. expectType<JSX.Element>(<MyComponent aP2={3} aP1 z={'z'} />)
  620. // missing required props
  621. // @ts-expect-error
  622. expectError(<MyComponent />)
  623. // wrong prop types
  624. // @ts-expect-error
  625. expectError(<MyComponent aP2={'wrong type'} z={'z'} />)
  626. // @ts-expect-error
  627. expectError(<MyComponent aP1={3} />)
  628. })
  629. describe('extends with mixins', () => {
  630. const Mixin = defineComponent({
  631. props: {
  632. mP1: {
  633. type: String,
  634. default: 'mP1'
  635. },
  636. mP2: Boolean,
  637. mP3: {
  638. type: Boolean,
  639. required: true
  640. }
  641. },
  642. data() {
  643. return {
  644. a: 1
  645. }
  646. }
  647. })
  648. const Base = defineComponent({
  649. props: {
  650. p1: Boolean,
  651. p2: {
  652. type: Number,
  653. default: 2
  654. },
  655. p3: {
  656. type: Boolean,
  657. required: true
  658. }
  659. },
  660. data() {
  661. return {
  662. b: 2
  663. }
  664. },
  665. computed: {
  666. c(): number {
  667. return this.p2 + this.b
  668. }
  669. }
  670. })
  671. const MyComponent = defineComponent({
  672. extends: Base,
  673. mixins: [Mixin],
  674. props: {
  675. // required should make property non-void
  676. z: {
  677. type: String,
  678. required: true
  679. }
  680. },
  681. render() {
  682. const props = this.$props
  683. // props
  684. expectType<boolean | undefined>(props.p1)
  685. expectType<number>(props.p2)
  686. expectType<string>(props.z)
  687. expectType<string>(props.mP1)
  688. expectType<boolean | undefined>(props.mP2)
  689. const data = this.$data
  690. expectType<number>(data.a)
  691. expectType<number>(data.b)
  692. // should also expose declared props on `this`
  693. expectType<number>(this.a)
  694. expectType<number>(this.b)
  695. expectType<boolean | undefined>(this.p1)
  696. expectType<number>(this.p2)
  697. expectType<string>(this.mP1)
  698. expectType<boolean | undefined>(this.mP2)
  699. // setup context properties should be mutable
  700. this.a = 5
  701. return null
  702. }
  703. })
  704. // Test TSX
  705. expectType<JSX.Element>(<MyComponent mP1="p1" mP2 mP3 p1 p2={1} p3 z={'z'} />)
  706. // mP1, mP2, p1, and p2 have default value. these are not required
  707. expectType<JSX.Element>(<MyComponent mP3 p3 z={'z'} />)
  708. // missing required props
  709. // @ts-expect-error
  710. expectError(<MyComponent mP3 p3 /* z='z' */ />)
  711. // missing required props from mixin
  712. // @ts-expect-error
  713. expectError(<MyComponent /* mP3 */ p3 z="z" />)
  714. // missing required props from extends
  715. // @ts-expect-error
  716. expectError(<MyComponent mP3 /* p3 */ z="z" />)
  717. // wrong prop types
  718. // @ts-expect-error
  719. expectError(<MyComponent p2={'wrong type'} z={'z'} />)
  720. // @ts-expect-error
  721. expectError(<MyComponent mP1={3} />)
  722. })
  723. describe('compatibility w/ createApp', () => {
  724. const comp = defineComponent({})
  725. createApp(comp).mount('#hello')
  726. const comp2 = defineComponent({
  727. props: { foo: String }
  728. })
  729. createApp(comp2).mount('#hello')
  730. const comp3 = defineComponent({
  731. setup() {
  732. return {
  733. a: 1
  734. }
  735. }
  736. })
  737. createApp(comp3).mount('#hello')
  738. })
  739. describe('defineComponent', () => {
  740. test('should accept components defined with defineComponent', () => {
  741. const comp = defineComponent({})
  742. defineComponent({
  743. components: { comp }
  744. })
  745. })
  746. test('should accept class components with receiving constructor arguments', () => {
  747. class Comp {
  748. static __vccOpts = {}
  749. constructor(_props: { foo: string }) {}
  750. }
  751. defineComponent({
  752. components: { Comp }
  753. })
  754. })
  755. })
  756. describe('emits', () => {
  757. // Note: for TSX inference, ideally we want to map emits to onXXX props,
  758. // but that requires type-level string constant concatenation as suggested in
  759. // https://github.com/Microsoft/TypeScript/issues/12754
  760. // The workaround for TSX users is instead of using emits, declare onXXX props
  761. // and call them instead. Since `v-on:click` compiles to an `onClick` prop,
  762. // this would also support other users consuming the component in templates
  763. // with `v-on` listeners.
  764. // with object emits
  765. defineComponent({
  766. emits: {
  767. click: (n: number) => typeof n === 'number',
  768. input: (b: string) => b.length > 1
  769. },
  770. setup(props, { emit }) {
  771. emit('click', 1)
  772. emit('input', 'foo')
  773. // @ts-expect-error
  774. expectError(emit('nope'))
  775. // @ts-expect-error
  776. expectError(emit('click'))
  777. // @ts-expect-error
  778. expectError(emit('click', 'foo'))
  779. // @ts-expect-error
  780. expectError(emit('input'))
  781. // @ts-expect-error
  782. expectError(emit('input', 1))
  783. },
  784. created() {
  785. this.$emit('click', 1)
  786. this.$emit('input', 'foo')
  787. // @ts-expect-error
  788. expectError(this.$emit('nope'))
  789. // @ts-expect-error
  790. expectError(this.$emit('click'))
  791. // @ts-expect-error
  792. expectError(this.$emit('click', 'foo'))
  793. // @ts-expect-error
  794. expectError(this.$emit('input'))
  795. // @ts-expect-error
  796. expectError(this.$emit('input', 1))
  797. }
  798. })
  799. // with array emits
  800. defineComponent({
  801. emits: ['foo', 'bar'],
  802. setup(props, { emit }) {
  803. emit('foo')
  804. emit('foo', 123)
  805. emit('bar')
  806. // @ts-expect-error
  807. expectError(emit('nope'))
  808. },
  809. created() {
  810. this.$emit('foo')
  811. this.$emit('foo', 123)
  812. this.$emit('bar')
  813. // @ts-expect-error
  814. expectError(this.$emit('nope'))
  815. }
  816. })
  817. // without emits
  818. defineComponent({
  819. setup(props, { emit }) {
  820. emit('test', 1)
  821. emit('test')
  822. }
  823. })
  824. // emit should be valid when ComponentPublicInstance is used.
  825. const instance = {} as ComponentPublicInstance
  826. instance.$emit('test', 1)
  827. instance.$emit('test')
  828. // `this` should be void inside of emits validators
  829. defineComponent({
  830. props: ['bar'],
  831. emits: {
  832. foo(): boolean {
  833. // @ts-expect-error
  834. return this.bar === 3
  835. }
  836. }
  837. })
  838. })
  839. describe('componentOptions setup should be `SetupContext`', () => {
  840. expect<ComponentOptions['setup']>({} as (
  841. props: Record<string, any>,
  842. ctx: SetupContext
  843. ) => any)
  844. })
  845. describe('extract instance type', () => {
  846. const Base = defineComponent({
  847. props: {
  848. baseA: {
  849. type: Number,
  850. default: 1
  851. }
  852. }
  853. })
  854. const MixinA = defineComponent({
  855. props: {
  856. mA: {
  857. type: String,
  858. default: ''
  859. }
  860. }
  861. })
  862. const CompA = defineComponent({
  863. extends: Base,
  864. mixins: [MixinA],
  865. props: {
  866. a: {
  867. type: Boolean,
  868. default: false
  869. },
  870. b: {
  871. type: String,
  872. required: true
  873. },
  874. c: Number
  875. }
  876. })
  877. const compA = {} as InstanceType<typeof CompA>
  878. expectType<boolean>(compA.a)
  879. expectType<string>(compA.b)
  880. expectType<number | undefined>(compA.c)
  881. // mixins
  882. expectType<string>(compA.mA)
  883. // extends
  884. expectType<number>(compA.baseA)
  885. // @ts-expect-error
  886. expectError((compA.a = true))
  887. // @ts-expect-error
  888. expectError((compA.b = 'foo'))
  889. // @ts-expect-error
  890. expectError((compA.c = 1))
  891. // @ts-expect-error
  892. expectError((compA.mA = 'foo'))
  893. // @ts-expect-error
  894. expectError((compA.baseA = 1))
  895. })
  896. describe('async setup', () => {
  897. type GT = string & { __brand: unknown }
  898. const Comp = defineComponent({
  899. async setup() {
  900. // setup context
  901. return {
  902. a: ref(1),
  903. b: {
  904. c: ref('hi')
  905. },
  906. d: reactive({
  907. e: ref('hello' as GT)
  908. })
  909. }
  910. },
  911. render() {
  912. // assert setup context unwrapping
  913. expectType<number>(this.a)
  914. expectType<string>(this.b.c.value)
  915. expectType<GT>(this.d.e)
  916. // setup context properties should be mutable
  917. this.a = 2
  918. }
  919. })
  920. const vm = {} as InstanceType<typeof Comp>
  921. // assert setup context unwrapping
  922. expectType<number>(vm.a)
  923. expectType<string>(vm.b.c.value)
  924. expectType<GT>(vm.d.e)
  925. // setup context properties should be mutable
  926. vm.a = 2
  927. })
  928. // check if defineComponent can be exported
  929. export default {
  930. // function components
  931. a: defineComponent(_ => h('div')),
  932. // no props
  933. b: defineComponent({
  934. data() {
  935. return {}
  936. }
  937. }),
  938. c: defineComponent({
  939. props: ['a']
  940. }),
  941. d: defineComponent({
  942. props: {
  943. a: Number
  944. }
  945. })
  946. }