defineComponent.test-d.tsx 28 KB

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