defineComponent.test-d.tsx 24 KB

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