defineComponent.test-d.tsx 27 KB

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