defineComponent.test-d.tsx 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059
  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. // #3468
  723. const CompWithD = defineComponent({
  724. data() {
  725. return { foo: 1 }
  726. }
  727. })
  728. const CompWithC = defineComponent({
  729. computed: {
  730. foo() {
  731. return 1
  732. }
  733. }
  734. })
  735. const CompWithM = defineComponent({ methods: { foo() {} } })
  736. const CompEmpty = defineComponent({})
  737. defineComponent({
  738. mixins: [CompWithD, CompEmpty],
  739. mounted() {
  740. expectType<number>(this.foo)
  741. }
  742. })
  743. defineComponent({
  744. mixins: [CompWithC, CompEmpty],
  745. mounted() {
  746. expectType<number>(this.foo)
  747. }
  748. })
  749. defineComponent({
  750. mixins: [CompWithM, CompEmpty],
  751. mounted() {
  752. expectType<() => void>(this.foo)
  753. }
  754. })
  755. })
  756. describe('compatibility w/ createApp', () => {
  757. const comp = defineComponent({})
  758. createApp(comp).mount('#hello')
  759. const comp2 = defineComponent({
  760. props: { foo: String }
  761. })
  762. createApp(comp2).mount('#hello')
  763. const comp3 = defineComponent({
  764. setup() {
  765. return {
  766. a: 1
  767. }
  768. }
  769. })
  770. createApp(comp3).mount('#hello')
  771. })
  772. describe('defineComponent', () => {
  773. test('should accept components defined with defineComponent', () => {
  774. const comp = defineComponent({})
  775. defineComponent({
  776. components: { comp }
  777. })
  778. })
  779. test('should accept class components with receiving constructor arguments', () => {
  780. class Comp {
  781. static __vccOpts = {}
  782. constructor(_props: { foo: string }) {}
  783. }
  784. defineComponent({
  785. components: { Comp }
  786. })
  787. })
  788. })
  789. describe('emits', () => {
  790. // Note: for TSX inference, ideally we want to map emits to onXXX props,
  791. // but that requires type-level string constant concatenation as suggested in
  792. // https://github.com/Microsoft/TypeScript/issues/12754
  793. // The workaround for TSX users is instead of using emits, declare onXXX props
  794. // and call them instead. Since `v-on:click` compiles to an `onClick` prop,
  795. // this would also support other users consuming the component in templates
  796. // with `v-on` listeners.
  797. // with object emits
  798. defineComponent({
  799. emits: {
  800. click: (n: number) => typeof n === 'number',
  801. input: (b: string) => b.length > 1
  802. },
  803. setup(props, { emit }) {
  804. emit('click', 1)
  805. emit('input', 'foo')
  806. // @ts-expect-error
  807. expectError(emit('nope'))
  808. // @ts-expect-error
  809. expectError(emit('click'))
  810. // @ts-expect-error
  811. expectError(emit('click', 'foo'))
  812. // @ts-expect-error
  813. expectError(emit('input'))
  814. // @ts-expect-error
  815. expectError(emit('input', 1))
  816. },
  817. created() {
  818. this.$emit('click', 1)
  819. this.$emit('input', 'foo')
  820. // @ts-expect-error
  821. expectError(this.$emit('nope'))
  822. // @ts-expect-error
  823. expectError(this.$emit('click'))
  824. // @ts-expect-error
  825. expectError(this.$emit('click', 'foo'))
  826. // @ts-expect-error
  827. expectError(this.$emit('input'))
  828. // @ts-expect-error
  829. expectError(this.$emit('input', 1))
  830. }
  831. })
  832. // with array emits
  833. defineComponent({
  834. emits: ['foo', 'bar'],
  835. setup(props, { emit }) {
  836. emit('foo')
  837. emit('foo', 123)
  838. emit('bar')
  839. // @ts-expect-error
  840. expectError(emit('nope'))
  841. },
  842. created() {
  843. this.$emit('foo')
  844. this.$emit('foo', 123)
  845. this.$emit('bar')
  846. // @ts-expect-error
  847. expectError(this.$emit('nope'))
  848. }
  849. })
  850. // without emits
  851. defineComponent({
  852. setup(props, { emit }) {
  853. emit('test', 1)
  854. emit('test')
  855. }
  856. })
  857. // emit should be valid when ComponentPublicInstance is used.
  858. const instance = {} as ComponentPublicInstance
  859. instance.$emit('test', 1)
  860. instance.$emit('test')
  861. // `this` should be void inside of emits validators
  862. defineComponent({
  863. props: ['bar'],
  864. emits: {
  865. foo(): boolean {
  866. // @ts-expect-error
  867. return this.bar === 3
  868. }
  869. }
  870. })
  871. })
  872. describe('componentOptions setup should be `SetupContext`', () => {
  873. expect<ComponentOptions['setup']>({} as (
  874. props: Record<string, any>,
  875. ctx: SetupContext
  876. ) => any)
  877. })
  878. describe('extract instance type', () => {
  879. const Base = defineComponent({
  880. props: {
  881. baseA: {
  882. type: Number,
  883. default: 1
  884. }
  885. }
  886. })
  887. const MixinA = defineComponent({
  888. props: {
  889. mA: {
  890. type: String,
  891. default: ''
  892. }
  893. }
  894. })
  895. const CompA = defineComponent({
  896. extends: Base,
  897. mixins: [MixinA],
  898. props: {
  899. a: {
  900. type: Boolean,
  901. default: false
  902. },
  903. b: {
  904. type: String,
  905. required: true
  906. },
  907. c: Number
  908. }
  909. })
  910. const compA = {} as InstanceType<typeof CompA>
  911. expectType<boolean>(compA.a)
  912. expectType<string>(compA.b)
  913. expectType<number | undefined>(compA.c)
  914. // mixins
  915. expectType<string>(compA.mA)
  916. // extends
  917. expectType<number>(compA.baseA)
  918. // @ts-expect-error
  919. expectError((compA.a = true))
  920. // @ts-expect-error
  921. expectError((compA.b = 'foo'))
  922. // @ts-expect-error
  923. expectError((compA.c = 1))
  924. // @ts-expect-error
  925. expectError((compA.mA = 'foo'))
  926. // @ts-expect-error
  927. expectError((compA.baseA = 1))
  928. })
  929. describe('async setup', () => {
  930. type GT = string & { __brand: unknown }
  931. const Comp = defineComponent({
  932. async setup() {
  933. // setup context
  934. return {
  935. a: ref(1),
  936. b: {
  937. c: ref('hi')
  938. },
  939. d: reactive({
  940. e: ref('hello' as GT)
  941. })
  942. }
  943. },
  944. render() {
  945. // assert setup context unwrapping
  946. expectType<number>(this.a)
  947. expectType<string>(this.b.c.value)
  948. expectType<GT>(this.d.e)
  949. // setup context properties should be mutable
  950. this.a = 2
  951. }
  952. })
  953. const vm = {} as InstanceType<typeof Comp>
  954. // assert setup context unwrapping
  955. expectType<number>(vm.a)
  956. expectType<string>(vm.b.c.value)
  957. expectType<GT>(vm.d.e)
  958. // setup context properties should be mutable
  959. vm.a = 2
  960. })
  961. // check if defineComponent can be exported
  962. export default {
  963. // function components
  964. a: defineComponent(_ => h('div')),
  965. // no props
  966. b: defineComponent({
  967. data() {
  968. return {}
  969. }
  970. }),
  971. c: defineComponent({
  972. props: ['a']
  973. }),
  974. d: defineComponent({
  975. props: {
  976. a: Number
  977. }
  978. })
  979. }