defineComponent.test-d.tsx 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071
  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. })
  844. // with array emits
  845. defineComponent({
  846. emits: ['foo', 'bar'],
  847. setup(props, { emit }) {
  848. emit('foo')
  849. emit('foo', 123)
  850. emit('bar')
  851. // @ts-expect-error
  852. expectError(emit('nope'))
  853. },
  854. created() {
  855. this.$emit('foo')
  856. this.$emit('foo', 123)
  857. this.$emit('bar')
  858. // @ts-expect-error
  859. expectError(this.$emit('nope'))
  860. }
  861. })
  862. // without emits
  863. defineComponent({
  864. setup(props, { emit }) {
  865. emit('test', 1)
  866. emit('test')
  867. }
  868. })
  869. // emit should be valid when ComponentPublicInstance is used.
  870. const instance = {} as ComponentPublicInstance
  871. instance.$emit('test', 1)
  872. instance.$emit('test')
  873. // `this` should be void inside of emits validators
  874. defineComponent({
  875. props: ['bar'],
  876. emits: {
  877. foo(): boolean {
  878. // @ts-expect-error
  879. return this.bar === 3
  880. }
  881. }
  882. })
  883. })
  884. describe('componentOptions setup should be `SetupContext`', () => {
  885. expect<ComponentOptions['setup']>({} as (
  886. props: Record<string, any>,
  887. ctx: SetupContext
  888. ) => any)
  889. })
  890. describe('extract instance type', () => {
  891. const Base = defineComponent({
  892. props: {
  893. baseA: {
  894. type: Number,
  895. default: 1
  896. }
  897. }
  898. })
  899. const MixinA = defineComponent({
  900. props: {
  901. mA: {
  902. type: String,
  903. default: ''
  904. }
  905. }
  906. })
  907. const CompA = defineComponent({
  908. extends: Base,
  909. mixins: [MixinA],
  910. props: {
  911. a: {
  912. type: Boolean,
  913. default: false
  914. },
  915. b: {
  916. type: String,
  917. required: true
  918. },
  919. c: Number
  920. }
  921. })
  922. const compA = {} as InstanceType<typeof CompA>
  923. expectType<boolean>(compA.a)
  924. expectType<string>(compA.b)
  925. expectType<number | undefined>(compA.c)
  926. // mixins
  927. expectType<string>(compA.mA)
  928. // extends
  929. expectType<number>(compA.baseA)
  930. // @ts-expect-error
  931. expectError((compA.a = true))
  932. // @ts-expect-error
  933. expectError((compA.b = 'foo'))
  934. // @ts-expect-error
  935. expectError((compA.c = 1))
  936. // @ts-expect-error
  937. expectError((compA.mA = 'foo'))
  938. // @ts-expect-error
  939. expectError((compA.baseA = 1))
  940. })
  941. describe('async setup', () => {
  942. type GT = string & { __brand: unknown }
  943. const Comp = defineComponent({
  944. async setup() {
  945. // setup context
  946. return {
  947. a: ref(1),
  948. b: {
  949. c: ref('hi')
  950. },
  951. d: reactive({
  952. e: ref('hello' as GT)
  953. })
  954. }
  955. },
  956. render() {
  957. // assert setup context unwrapping
  958. expectType<number>(this.a)
  959. expectType<string>(this.b.c.value)
  960. expectType<GT>(this.d.e)
  961. // setup context properties should be mutable
  962. this.a = 2
  963. }
  964. })
  965. const vm = {} as InstanceType<typeof Comp>
  966. // assert setup context unwrapping
  967. expectType<number>(vm.a)
  968. expectType<string>(vm.b.c.value)
  969. expectType<GT>(vm.d.e)
  970. // setup context properties should be mutable
  971. vm.a = 2
  972. })
  973. // check if defineComponent can be exported
  974. export default {
  975. // function components
  976. a: defineComponent(_ => h('div')),
  977. // no props
  978. b: defineComponent({
  979. data() {
  980. return {}
  981. }
  982. }),
  983. c: defineComponent({
  984. props: ['a']
  985. }),
  986. d: defineComponent({
  987. props: {
  988. a: Number
  989. }
  990. })
  991. }