defineComponent.test-d.tsx 22 KB

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