defineComponent.test-d.tsx 36 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535
  1. import {
  2. type Component,
  3. type ComponentOptions,
  4. type ComponentPublicInstance,
  5. type PropType,
  6. type SetupContext,
  7. type Slots,
  8. type SlotsType,
  9. type VNode,
  10. createApp,
  11. defineComponent,
  12. h,
  13. reactive,
  14. ref,
  15. withKeys,
  16. withModifiers,
  17. } from 'vue'
  18. import { type IsUnion, describe, expectType } from './utils'
  19. describe('with object props', () => {
  20. interface ExpectedProps {
  21. a?: number | undefined
  22. b: string
  23. e?: Function
  24. h: boolean
  25. j: undefined | (() => string | undefined)
  26. bb: string
  27. bbb: string
  28. bbbb: string | undefined
  29. bbbbb: string | undefined
  30. cc?: string[] | undefined
  31. dd: { n: 1 }
  32. ee?: () => string
  33. ff?: (a: number, b: string) => { a: boolean }
  34. ccc?: string[] | undefined
  35. ddd: string[]
  36. eee: () => { a: string }
  37. fff: (a: number, b: string) => { a: boolean }
  38. hhh: boolean
  39. ggg: 'foo' | 'bar'
  40. ffff: (a: number, b: string) => { a: boolean }
  41. iii?: (() => string) | (() => number)
  42. jjj: ((arg1: string) => string) | ((arg1: string, arg2: string) => string)
  43. kkk?: any
  44. validated?: string
  45. date?: Date
  46. l?: Date
  47. ll?: Date | number
  48. lll?: string | number
  49. }
  50. type GT = string & { __brand: unknown }
  51. const props = {
  52. a: Number,
  53. // required should make property non-void
  54. b: {
  55. type: String,
  56. required: true as true,
  57. },
  58. e: Function,
  59. h: Boolean,
  60. j: Function as PropType<undefined | (() => string | undefined)>,
  61. // default value should infer type and make it non-void
  62. bb: {
  63. default: 'hello',
  64. },
  65. bbb: {
  66. // Note: default function value requires arrow syntax + explicit
  67. // annotation
  68. default: (props: any) => (props.bb as string) || 'foo',
  69. },
  70. bbbb: {
  71. type: String,
  72. default: undefined,
  73. },
  74. bbbbb: {
  75. type: String,
  76. default: () => undefined,
  77. },
  78. // explicit type casting
  79. cc: Array as PropType<string[]>,
  80. // required + type casting
  81. dd: {
  82. type: Object as PropType<{ n: 1 }>,
  83. required: true as true,
  84. },
  85. // return type
  86. ee: Function as PropType<() => string>,
  87. // arguments + object return
  88. ff: Function as PropType<(a: number, b: string) => { a: boolean }>,
  89. // explicit type casting with constructor
  90. ccc: Array as () => string[],
  91. // required + constructor type casting
  92. ddd: {
  93. type: Array as () => string[],
  94. required: true as true,
  95. },
  96. // required + object return
  97. eee: {
  98. type: Function as PropType<() => { a: string }>,
  99. required: true as true,
  100. },
  101. // required + arguments + object return
  102. fff: {
  103. type: Function as PropType<(a: number, b: string) => { a: boolean }>,
  104. required: true as true,
  105. },
  106. hhh: {
  107. type: Boolean,
  108. required: true as true,
  109. },
  110. // default + type casting
  111. ggg: {
  112. type: String as PropType<'foo' | 'bar'>,
  113. default: 'foo',
  114. },
  115. // default + function
  116. ffff: {
  117. type: Function as PropType<(a: number, b: string) => { a: boolean }>,
  118. default: (a: number, b: string) => ({ a: a > +b }),
  119. },
  120. // union + function with different return types
  121. iii: Function as PropType<(() => string) | (() => number)>,
  122. // union + function with different args & same return type
  123. jjj: {
  124. type: Function as PropType<
  125. ((arg1: string) => string) | ((arg1: string, arg2: string) => string)
  126. >,
  127. required: true as true,
  128. },
  129. kkk: null,
  130. validated: {
  131. type: String,
  132. // validator requires explicit annotation
  133. validator: (val: unknown) => val !== '',
  134. },
  135. date: Date,
  136. l: [Date],
  137. ll: [Date, Number],
  138. lll: [String, Number],
  139. }
  140. const MyComponent = defineComponent({
  141. props,
  142. setup(props) {
  143. // type assertion. See https://github.com/SamVerschueren/tsd
  144. expectType<ExpectedProps['a']>(props.a)
  145. expectType<ExpectedProps['b']>(props.b)
  146. expectType<ExpectedProps['e']>(props.e)
  147. expectType<ExpectedProps['h']>(props.h)
  148. expectType<ExpectedProps['j']>(props.j)
  149. expectType<ExpectedProps['bb']>(props.bb)
  150. expectType<ExpectedProps['bbb']>(props.bbb)
  151. expectType<ExpectedProps['bbbb']>(props.bbbb)
  152. expectType<ExpectedProps['bbbbb']>(props.bbbbb)
  153. expectType<ExpectedProps['cc']>(props.cc)
  154. expectType<ExpectedProps['dd']>(props.dd)
  155. expectType<ExpectedProps['ee']>(props.ee)
  156. expectType<ExpectedProps['ff']>(props.ff)
  157. expectType<ExpectedProps['ccc']>(props.ccc)
  158. expectType<ExpectedProps['ddd']>(props.ddd)
  159. expectType<ExpectedProps['eee']>(props.eee)
  160. expectType<ExpectedProps['fff']>(props.fff)
  161. expectType<ExpectedProps['hhh']>(props.hhh)
  162. expectType<ExpectedProps['ggg']>(props.ggg)
  163. expectType<ExpectedProps['ffff']>(props.ffff)
  164. if (typeof props.iii !== 'function') {
  165. expectType<undefined>(props.iii)
  166. }
  167. expectType<ExpectedProps['iii']>(props.iii)
  168. expectType<IsUnion<typeof props.jjj>>(true)
  169. expectType<ExpectedProps['jjj']>(props.jjj)
  170. expectType<ExpectedProps['kkk']>(props.kkk)
  171. expectType<ExpectedProps['validated']>(props.validated)
  172. expectType<ExpectedProps['date']>(props.date)
  173. expectType<ExpectedProps['l']>(props.l)
  174. expectType<ExpectedProps['ll']>(props.ll)
  175. expectType<ExpectedProps['lll']>(props.lll)
  176. // @ts-expect-error props should be readonly
  177. props.a = 1
  178. // setup context
  179. return {
  180. c: ref(1),
  181. d: {
  182. e: ref('hi'),
  183. },
  184. f: reactive({
  185. g: ref('hello' as GT),
  186. }),
  187. }
  188. },
  189. provide() {
  190. return {}
  191. },
  192. render() {
  193. const props = this.$props
  194. expectType<ExpectedProps['a']>(props.a)
  195. expectType<ExpectedProps['b']>(props.b)
  196. expectType<ExpectedProps['e']>(props.e)
  197. expectType<ExpectedProps['h']>(props.h)
  198. expectType<ExpectedProps['bb']>(props.bb)
  199. expectType<ExpectedProps['cc']>(props.cc)
  200. expectType<ExpectedProps['dd']>(props.dd)
  201. expectType<ExpectedProps['ee']>(props.ee)
  202. expectType<ExpectedProps['ff']>(props.ff)
  203. expectType<ExpectedProps['ccc']>(props.ccc)
  204. expectType<ExpectedProps['ddd']>(props.ddd)
  205. expectType<ExpectedProps['eee']>(props.eee)
  206. expectType<ExpectedProps['fff']>(props.fff)
  207. expectType<ExpectedProps['hhh']>(props.hhh)
  208. expectType<ExpectedProps['ggg']>(props.ggg)
  209. if (typeof props.iii !== 'function') {
  210. expectType<undefined>(props.iii)
  211. }
  212. expectType<ExpectedProps['iii']>(props.iii)
  213. expectType<IsUnion<typeof props.jjj>>(true)
  214. expectType<ExpectedProps['jjj']>(props.jjj)
  215. expectType<ExpectedProps['kkk']>(props.kkk)
  216. // @ts-expect-error props should be readonly
  217. props.a = 1
  218. // should also expose declared props on `this`
  219. expectType<ExpectedProps['a']>(this.a)
  220. expectType<ExpectedProps['b']>(this.b)
  221. expectType<ExpectedProps['e']>(this.e)
  222. expectType<ExpectedProps['h']>(this.h)
  223. expectType<ExpectedProps['bb']>(this.bb)
  224. expectType<ExpectedProps['cc']>(this.cc)
  225. expectType<ExpectedProps['dd']>(this.dd)
  226. expectType<ExpectedProps['ee']>(this.ee)
  227. expectType<ExpectedProps['ff']>(this.ff)
  228. expectType<ExpectedProps['ccc']>(this.ccc)
  229. expectType<ExpectedProps['ddd']>(this.ddd)
  230. expectType<ExpectedProps['eee']>(this.eee)
  231. expectType<ExpectedProps['fff']>(this.fff)
  232. expectType<ExpectedProps['hhh']>(this.hhh)
  233. expectType<ExpectedProps['ggg']>(this.ggg)
  234. if (typeof this.iii !== 'function') {
  235. expectType<undefined>(this.iii)
  236. }
  237. expectType<ExpectedProps['iii']>(this.iii)
  238. const { jjj } = this
  239. expectType<IsUnion<typeof jjj>>(true)
  240. expectType<ExpectedProps['jjj']>(this.jjj)
  241. expectType<ExpectedProps['kkk']>(this.kkk)
  242. // @ts-expect-error props on `this` should be readonly
  243. this.a = 1
  244. // assert setup context unwrapping
  245. expectType<number>(this.c)
  246. expectType<string>(this.d.e.value)
  247. expectType<GT>(this.f.g)
  248. // setup context properties should be mutable
  249. this.c = 2
  250. return null
  251. },
  252. })
  253. expectType<Component>(MyComponent)
  254. // Test TSX
  255. expectType<JSX.Element>(
  256. <MyComponent
  257. a={1}
  258. b="b"
  259. bb="bb"
  260. e={() => {}}
  261. cc={['cc']}
  262. dd={{ n: 1 }}
  263. ee={() => 'ee'}
  264. ccc={['ccc']}
  265. ddd={['ddd']}
  266. eee={() => ({ a: 'eee' })}
  267. fff={(a, b) => ({ a: a > +b })}
  268. hhh={false}
  269. ggg="foo"
  270. jjj={() => ''}
  271. // should allow class/style as attrs
  272. class="bar"
  273. style={{ color: 'red' }}
  274. // should allow key
  275. key={'foo'}
  276. // should allow ref
  277. ref={'foo'}
  278. ref_for={true}
  279. />,
  280. )
  281. expectType<Component>(
  282. <MyComponent
  283. b="b"
  284. dd={{ n: 1 }}
  285. ddd={['ddd']}
  286. eee={() => ({ a: 'eee' })}
  287. fff={(a, b) => ({ a: a > +b })}
  288. hhh={false}
  289. jjj={() => ''}
  290. />,
  291. )
  292. // @ts-expect-error missing required props
  293. let c = <MyComponent />
  294. // @ts-expect-error wrong prop types
  295. c = <MyComponent a={'wrong type'} b="foo" dd={{ n: 1 }} ddd={['foo']} />
  296. // @ts-expect-error wrong prop types
  297. c = <MyComponent ggg="baz" />
  298. // @ts-expect-error
  299. ;<MyComponent b="foo" dd={{ n: 'string' }} ddd={['foo']} />
  300. // `this` should be void inside of prop validators and prop default factories
  301. defineComponent({
  302. props: {
  303. myProp: {
  304. type: Number,
  305. validator(val: unknown): boolean {
  306. // @ts-expect-error
  307. return val !== this.otherProp
  308. },
  309. default(): number {
  310. // @ts-expect-error
  311. return this.otherProp + 1
  312. },
  313. },
  314. otherProp: {
  315. type: Number,
  316. required: true,
  317. },
  318. },
  319. })
  320. })
  321. describe('type inference w/ optional props declaration', () => {
  322. const MyComponent = defineComponent<{ a: string[]; msg: string }>({
  323. setup(props) {
  324. expectType<string>(props.msg)
  325. expectType<string[]>(props.a)
  326. return {
  327. b: 1,
  328. }
  329. },
  330. })
  331. expectType<JSX.Element>(<MyComponent msg="1" a={['1']} />)
  332. // @ts-expect-error
  333. ;<MyComponent />
  334. // @ts-expect-error
  335. ;<MyComponent msg="1" />
  336. })
  337. describe('type inference w/ direct setup function', () => {
  338. const MyComponent = defineComponent((_props: { msg: string }) => () => {})
  339. expectType<JSX.Element>(<MyComponent msg="foo" />)
  340. // @ts-expect-error
  341. ;<MyComponent />
  342. // @ts-expect-error
  343. ;<MyComponent msg={1} />
  344. })
  345. describe('type inference w/ array props declaration', () => {
  346. const MyComponent = defineComponent({
  347. props: ['a', 'b'],
  348. setup(props) {
  349. // @ts-expect-error props should be readonly
  350. props.a = 1
  351. expectType<any>(props.a)
  352. expectType<any>(props.b)
  353. return {
  354. c: 1,
  355. }
  356. },
  357. render() {
  358. expectType<any>(this.$props.a)
  359. expectType<any>(this.$props.b)
  360. // @ts-expect-error
  361. this.$props.a = 1
  362. expectType<any>(this.a)
  363. expectType<any>(this.b)
  364. expectType<number>(this.c)
  365. },
  366. })
  367. expectType<JSX.Element>(<MyComponent a={[1, 2]} b="b" />)
  368. // @ts-expect-error
  369. ;<MyComponent other="other" />
  370. })
  371. describe('type inference w/ options API', () => {
  372. defineComponent({
  373. props: { a: Number },
  374. setup() {
  375. return {
  376. b: 123,
  377. }
  378. },
  379. data() {
  380. // Limitation: we cannot expose the return result of setup() on `this`
  381. // here in data() - somehow that would mess up the inference
  382. expectType<number | undefined>(this.a)
  383. return {
  384. c: this.a || 123,
  385. someRef: ref(0),
  386. }
  387. },
  388. computed: {
  389. d() {
  390. expectType<number>(this.b)
  391. return this.b + 1
  392. },
  393. e: {
  394. get() {
  395. expectType<number>(this.b)
  396. expectType<number>(this.d)
  397. return this.b + this.d
  398. },
  399. set(v: number) {
  400. expectType<number>(this.b)
  401. expectType<number>(this.d)
  402. expectType<number>(v)
  403. },
  404. },
  405. },
  406. watch: {
  407. a() {
  408. expectType<number>(this.b)
  409. this.b + 1
  410. },
  411. },
  412. created() {
  413. // props
  414. expectType<number | undefined>(this.a)
  415. // returned from setup()
  416. expectType<number>(this.b)
  417. // returned from data()
  418. expectType<number>(this.c)
  419. // computed
  420. expectType<number>(this.d)
  421. // computed get/set
  422. expectType<number>(this.e)
  423. expectType<number>(this.someRef)
  424. },
  425. methods: {
  426. doSomething() {
  427. // props
  428. expectType<number | undefined>(this.a)
  429. // returned from setup()
  430. expectType<number>(this.b)
  431. // returned from data()
  432. expectType<number>(this.c)
  433. // computed
  434. expectType<number>(this.d)
  435. // computed get/set
  436. expectType<number>(this.e)
  437. },
  438. returnSomething() {
  439. return this.a
  440. },
  441. },
  442. render() {
  443. // props
  444. expectType<number | undefined>(this.a)
  445. // returned from setup()
  446. expectType<number>(this.b)
  447. // returned from data()
  448. expectType<number>(this.c)
  449. // computed
  450. expectType<number>(this.d)
  451. // computed get/set
  452. expectType<number>(this.e)
  453. // method
  454. expectType<() => number | undefined>(this.returnSomething)
  455. },
  456. })
  457. })
  458. describe('with mixins', () => {
  459. const MixinA = defineComponent({
  460. emits: ['bar'],
  461. props: {
  462. aP1: {
  463. type: String,
  464. default: 'aP1',
  465. },
  466. aP2: Boolean,
  467. },
  468. data() {
  469. return {
  470. a: 1,
  471. }
  472. },
  473. })
  474. const MixinB = defineComponent({
  475. props: ['bP1', 'bP2'],
  476. data() {
  477. return {
  478. b: 2,
  479. }
  480. },
  481. })
  482. const MixinC = defineComponent({
  483. data() {
  484. return {
  485. c: 3,
  486. }
  487. },
  488. })
  489. const MixinD = defineComponent({
  490. mixins: [MixinA],
  491. data() {
  492. //@ts-expect-error computed are not available on data()
  493. expectError<number>(this.dC1)
  494. //@ts-expect-error computed are not available on data()
  495. expectError<string>(this.dC2)
  496. return {
  497. d: 4,
  498. }
  499. },
  500. setup(props) {
  501. expectType<string>(props.aP1)
  502. },
  503. computed: {
  504. dC1() {
  505. return this.d + this.a
  506. },
  507. dC2() {
  508. return this.aP1 + 'dC2'
  509. },
  510. },
  511. })
  512. const MyComponent = defineComponent({
  513. mixins: [MixinA, MixinB, MixinC, MixinD],
  514. emits: ['click'],
  515. props: {
  516. // required should make property non-void
  517. z: {
  518. type: String,
  519. required: true,
  520. },
  521. },
  522. data(vm) {
  523. expectType<number>(vm.a)
  524. expectType<number>(vm.b)
  525. expectType<number>(vm.c)
  526. expectType<number>(vm.d)
  527. // should also expose declared props on `this`
  528. expectType<number>(this.a)
  529. expectType<string>(this.aP1)
  530. expectType<boolean | undefined>(this.aP2)
  531. expectType<number>(this.b)
  532. expectType<any>(this.bP1)
  533. expectType<number>(this.c)
  534. expectType<number>(this.d)
  535. return {}
  536. },
  537. setup(props) {
  538. expectType<string>(props.z)
  539. // props
  540. expectType<((...args: any[]) => any) | undefined>(props.onClick)
  541. // from MixinA
  542. expectType<((...args: any[]) => any) | undefined>(props.onBar)
  543. expectType<string>(props.aP1)
  544. expectType<boolean | undefined>(props.aP2)
  545. expectType<any>(props.bP1)
  546. expectType<any>(props.bP2)
  547. expectType<string>(props.z)
  548. },
  549. render() {
  550. const props = this.$props
  551. // props
  552. expectType<((...args: any[]) => any) | undefined>(props.onClick)
  553. // from MixinA
  554. expectType<((...args: any[]) => any) | undefined>(props.onBar)
  555. expectType<string>(props.aP1)
  556. expectType<boolean | undefined>(props.aP2)
  557. expectType<any>(props.bP1)
  558. expectType<any>(props.bP2)
  559. expectType<string>(props.z)
  560. const data = this.$data
  561. expectType<number>(data.a)
  562. expectType<number>(data.b)
  563. expectType<number>(data.c)
  564. expectType<number>(data.d)
  565. // should also expose declared props on `this`
  566. expectType<number>(this.a)
  567. expectType<string>(this.aP1)
  568. expectType<boolean | undefined>(this.aP2)
  569. expectType<number>(this.b)
  570. expectType<any>(this.bP1)
  571. expectType<number>(this.c)
  572. expectType<number>(this.d)
  573. expectType<number>(this.dC1)
  574. expectType<string>(this.dC2)
  575. // props should be readonly
  576. // @ts-expect-error
  577. this.aP1 = 'new'
  578. // @ts-expect-error
  579. this.z = 1
  580. // props on `this` should be readonly
  581. // @ts-expect-error
  582. this.bP1 = 1
  583. // string value can not assigned to number type value
  584. // @ts-expect-error
  585. this.c = '1'
  586. // setup context properties should be mutable
  587. this.d = 5
  588. return null
  589. },
  590. })
  591. // Test TSX
  592. expectType<JSX.Element>(
  593. <MyComponent aP1={'aP'} aP2 bP1={1} bP2={[1, 2]} z={'z'} />,
  594. )
  595. // missing required props
  596. // @ts-expect-error
  597. ;<MyComponent />
  598. // wrong prop types
  599. // @ts-expect-error
  600. ;<MyComponent aP1="ap" aP2={'wrong type'} bP1="b" z={'z'} />
  601. // @ts-expect-error
  602. ;<MyComponent aP1={1} bP2={[1]} />
  603. })
  604. describe('with extends', () => {
  605. const Base = defineComponent({
  606. props: {
  607. aP1: Boolean,
  608. aP2: {
  609. type: Number,
  610. default: 2,
  611. },
  612. },
  613. data() {
  614. return {
  615. a: 1,
  616. }
  617. },
  618. computed: {
  619. c(): number {
  620. return this.aP2 + this.a
  621. },
  622. },
  623. })
  624. const MyComponent = defineComponent({
  625. extends: Base,
  626. props: {
  627. // required should make property non-void
  628. z: {
  629. type: String,
  630. required: true,
  631. },
  632. },
  633. render() {
  634. const props = this.$props
  635. // props
  636. expectType<boolean | undefined>(props.aP1)
  637. expectType<number>(props.aP2)
  638. expectType<string>(props.z)
  639. const data = this.$data
  640. expectType<number>(data.a)
  641. // should also expose declared props on `this`
  642. expectType<number>(this.a)
  643. expectType<boolean | undefined>(this.aP1)
  644. expectType<number>(this.aP2)
  645. // setup context properties should be mutable
  646. this.a = 5
  647. return null
  648. },
  649. })
  650. // Test TSX
  651. expectType<JSX.Element>(<MyComponent aP2={3} aP1 z={'z'} />)
  652. // missing required props
  653. // @ts-expect-error
  654. ;<MyComponent />
  655. // wrong prop types
  656. // @ts-expect-error
  657. ;<MyComponent aP2={'wrong type'} z={'z'} />
  658. // @ts-expect-error
  659. ;<MyComponent aP1={3} />
  660. })
  661. describe('extends with mixins', () => {
  662. const Mixin = defineComponent({
  663. emits: ['bar'],
  664. props: {
  665. mP1: {
  666. type: String,
  667. default: 'mP1',
  668. },
  669. mP2: Boolean,
  670. mP3: {
  671. type: Boolean,
  672. required: true,
  673. },
  674. },
  675. data() {
  676. return {
  677. a: 1,
  678. }
  679. },
  680. })
  681. const Base = defineComponent({
  682. emits: ['foo'],
  683. props: {
  684. p1: Boolean,
  685. p2: {
  686. type: Number,
  687. default: 2,
  688. },
  689. p3: {
  690. type: Boolean,
  691. required: true,
  692. },
  693. },
  694. data() {
  695. return {
  696. b: 2,
  697. }
  698. },
  699. computed: {
  700. c(): number {
  701. return this.p2 + this.b
  702. },
  703. },
  704. })
  705. const MyComponent = defineComponent({
  706. extends: Base,
  707. mixins: [Mixin],
  708. emits: ['click'],
  709. props: {
  710. // required should make property non-void
  711. z: {
  712. type: String,
  713. required: true,
  714. },
  715. },
  716. render() {
  717. const props = this.$props
  718. // props
  719. expectType<((...args: any[]) => any) | undefined>(props.onClick)
  720. // from Mixin
  721. expectType<((...args: any[]) => any) | undefined>(props.onBar)
  722. // from Base
  723. expectType<((...args: any[]) => any) | undefined>(props.onFoo)
  724. expectType<boolean | undefined>(props.p1)
  725. expectType<number>(props.p2)
  726. expectType<string>(props.z)
  727. expectType<string>(props.mP1)
  728. expectType<boolean | undefined>(props.mP2)
  729. const data = this.$data
  730. expectType<number>(data.a)
  731. expectType<number>(data.b)
  732. // should also expose declared props on `this`
  733. expectType<number>(this.a)
  734. expectType<number>(this.b)
  735. expectType<boolean | undefined>(this.p1)
  736. expectType<number>(this.p2)
  737. expectType<string>(this.mP1)
  738. expectType<boolean | undefined>(this.mP2)
  739. // setup context properties should be mutable
  740. this.a = 5
  741. return null
  742. },
  743. })
  744. // Test TSX
  745. expectType<JSX.Element>(<MyComponent mP1="p1" mP2 mP3 p1 p2={1} p3 z={'z'} />)
  746. // mP1, mP2, p1, and p2 have default value. these are not required
  747. expectType<JSX.Element>(<MyComponent mP3 p3 z={'z'} />)
  748. // missing required props
  749. // @ts-expect-error
  750. ;<MyComponent mP3 p3 /* z='z' */ />
  751. // missing required props from mixin
  752. // @ts-expect-error
  753. ;<MyComponent /* mP3 */ p3 z="z" />
  754. // missing required props from extends
  755. // @ts-expect-error
  756. ;<MyComponent mP3 /* p3 */ z="z" />
  757. // wrong prop types
  758. // @ts-expect-error
  759. ;<MyComponent p2={'wrong type'} z={'z'} />
  760. // @ts-expect-error
  761. ;<MyComponent mP1={3} />
  762. // #3468
  763. const CompWithD = defineComponent({
  764. data() {
  765. return { foo: 1 }
  766. },
  767. })
  768. const CompWithC = defineComponent({
  769. computed: {
  770. foo() {
  771. return 1
  772. },
  773. },
  774. })
  775. const CompWithM = defineComponent({ methods: { foo() {} } })
  776. const CompEmpty = defineComponent({})
  777. defineComponent({
  778. mixins: [CompWithD, CompEmpty],
  779. mounted() {
  780. expectType<number>(this.foo)
  781. },
  782. })
  783. defineComponent({
  784. mixins: [CompWithC, CompEmpty],
  785. mounted() {
  786. expectType<number>(this.foo)
  787. },
  788. })
  789. defineComponent({
  790. mixins: [CompWithM, CompEmpty],
  791. mounted() {
  792. expectType<() => void>(this.foo)
  793. },
  794. })
  795. })
  796. describe('compatibility w/ createApp', () => {
  797. const comp = defineComponent({})
  798. createApp(comp).mount('#hello')
  799. const comp2 = defineComponent({
  800. props: { foo: String },
  801. })
  802. createApp(comp2).mount('#hello')
  803. const comp3 = defineComponent({
  804. setup() {
  805. return {
  806. a: 1,
  807. }
  808. },
  809. })
  810. createApp(comp3).mount('#hello')
  811. })
  812. describe('defineComponent', () => {
  813. describe('should accept components defined with defineComponent', () => {
  814. const comp = defineComponent({})
  815. defineComponent({
  816. components: { comp },
  817. })
  818. })
  819. describe('should accept class components with receiving constructor arguments', () => {
  820. class Comp {
  821. static __vccOpts = {}
  822. constructor(_props: { foo: string }) {}
  823. }
  824. defineComponent({
  825. components: { Comp },
  826. })
  827. })
  828. })
  829. describe('emits', () => {
  830. // Note: for TSX inference, ideally we want to map emits to onXXX props,
  831. // but that requires type-level string constant concatenation as suggested in
  832. // https://github.com/Microsoft/TypeScript/issues/12754
  833. // The workaround for TSX users is instead of using emits, declare onXXX props
  834. // and call them instead. Since `v-on:click` compiles to an `onClick` prop,
  835. // this would also support other users consuming the component in templates
  836. // with `v-on` listeners.
  837. // with object emits
  838. defineComponent({
  839. emits: {
  840. click: (n: number) => typeof n === 'number',
  841. input: (b: string) => b.length > 1,
  842. },
  843. setup(props, { emit }) {
  844. expectType<((n: number) => boolean) | undefined>(props.onClick)
  845. expectType<((b: string) => boolean) | undefined>(props.onInput)
  846. emit('click', 1)
  847. emit('input', 'foo')
  848. // @ts-expect-error
  849. emit('nope')
  850. // @ts-expect-error
  851. emit('click')
  852. // @ts-expect-error
  853. emit('click', 'foo')
  854. // @ts-expect-error
  855. emit('input')
  856. // @ts-expect-error
  857. emit('input', 1)
  858. },
  859. created() {
  860. this.$emit('click', 1)
  861. this.$emit('input', 'foo')
  862. // @ts-expect-error
  863. this.$emit('nope')
  864. // @ts-expect-error
  865. this.$emit('click')
  866. // @ts-expect-error
  867. this.$emit('click', 'foo')
  868. // @ts-expect-error
  869. this.$emit('input')
  870. // @ts-expect-error
  871. this.$emit('input', 1)
  872. },
  873. mounted() {
  874. // #3599
  875. this.$nextTick(function () {
  876. // this should be bound to this instance
  877. this.$emit('click', 1)
  878. this.$emit('input', 'foo')
  879. // @ts-expect-error
  880. this.$emit('nope')
  881. // @ts-expect-error
  882. this.$emit('click')
  883. // @ts-expect-error
  884. this.$emit('click', 'foo')
  885. // @ts-expect-error
  886. this.$emit('input')
  887. // @ts-expect-error
  888. this.$emit('input', 1)
  889. })
  890. },
  891. })
  892. // with array emits
  893. defineComponent({
  894. emits: ['foo', 'bar'],
  895. setup(props, { emit }) {
  896. expectType<((...args: any[]) => any) | undefined>(props.onFoo)
  897. expectType<((...args: any[]) => any) | undefined>(props.onBar)
  898. emit('foo')
  899. emit('foo', 123)
  900. emit('bar')
  901. // @ts-expect-error
  902. emit('nope')
  903. },
  904. created() {
  905. this.$emit('foo')
  906. this.$emit('foo', 123)
  907. this.$emit('bar')
  908. // @ts-expect-error
  909. this.$emit('nope')
  910. },
  911. })
  912. // with tsx
  913. const Component = defineComponent({
  914. emits: {
  915. click: (n: number) => typeof n === 'number',
  916. },
  917. setup(props, { emit }) {
  918. expectType<((n: number) => any) | undefined>(props.onClick)
  919. emit('click', 1)
  920. // @ts-expect-error
  921. emit('click')
  922. // @ts-expect-error
  923. emit('click', 'foo')
  924. },
  925. })
  926. defineComponent({
  927. render() {
  928. return (
  929. <Component
  930. onClick={(n: number) => {
  931. return n + 1
  932. }}
  933. />
  934. )
  935. },
  936. })
  937. // without emits
  938. defineComponent({
  939. setup(props, { emit }) {
  940. emit('test', 1)
  941. emit('test')
  942. },
  943. })
  944. // emit should be valid when ComponentPublicInstance is used.
  945. const instance = {} as ComponentPublicInstance
  946. instance.$emit('test', 1)
  947. instance.$emit('test')
  948. // `this` should be void inside of emits validators
  949. defineComponent({
  950. props: ['bar'],
  951. emits: {
  952. foo(): boolean {
  953. // @ts-expect-error
  954. return this.bar === 3
  955. },
  956. },
  957. })
  958. })
  959. describe('inject', () => {
  960. // with object inject
  961. defineComponent({
  962. props: {
  963. a: String,
  964. },
  965. inject: {
  966. foo: 'foo',
  967. bar: 'bar',
  968. },
  969. created() {
  970. expectType<unknown>(this.foo)
  971. expectType<unknown>(this.bar)
  972. // @ts-expect-error
  973. this.foobar = 1
  974. },
  975. })
  976. // with array inject
  977. defineComponent({
  978. props: ['a', 'b'],
  979. inject: ['foo', 'bar'],
  980. created() {
  981. expectType<unknown>(this.foo)
  982. expectType<unknown>(this.bar)
  983. // @ts-expect-error
  984. this.foobar = 1
  985. },
  986. })
  987. // with no props
  988. defineComponent({
  989. inject: {
  990. foo: {
  991. from: 'pfoo',
  992. default: 'foo',
  993. },
  994. bar: {
  995. from: 'pbar',
  996. default: 'bar',
  997. },
  998. },
  999. created() {
  1000. expectType<unknown>(this.foo)
  1001. expectType<unknown>(this.bar)
  1002. // @ts-expect-error
  1003. this.foobar = 1
  1004. },
  1005. })
  1006. // without inject
  1007. defineComponent({
  1008. props: ['a', 'b'],
  1009. created() {
  1010. // @ts-expect-error
  1011. this.foo = 1
  1012. // @ts-expect-error
  1013. this.bar = 1
  1014. },
  1015. })
  1016. })
  1017. describe('componentOptions setup should be `SetupContext`', () => {
  1018. expectType<ComponentOptions['setup']>(
  1019. {} as (props: Record<string, any>, ctx: SetupContext) => any,
  1020. )
  1021. })
  1022. describe('extract instance type', () => {
  1023. const Base = defineComponent({
  1024. props: {
  1025. baseA: {
  1026. type: Number,
  1027. default: 1,
  1028. },
  1029. },
  1030. })
  1031. const MixinA = defineComponent({
  1032. props: {
  1033. mA: {
  1034. type: String,
  1035. default: '',
  1036. },
  1037. },
  1038. })
  1039. const CompA = defineComponent({
  1040. extends: Base,
  1041. mixins: [MixinA],
  1042. props: {
  1043. a: {
  1044. type: Boolean,
  1045. default: false,
  1046. },
  1047. b: {
  1048. type: String,
  1049. required: true,
  1050. },
  1051. c: Number,
  1052. },
  1053. })
  1054. const compA = {} as InstanceType<typeof CompA>
  1055. expectType<boolean>(compA.a)
  1056. expectType<string>(compA.b)
  1057. expectType<number | undefined>(compA.c)
  1058. // mixins
  1059. expectType<string>(compA.mA)
  1060. // extends
  1061. expectType<number>(compA.baseA)
  1062. // @ts-expect-error
  1063. compA.a = true
  1064. // @ts-expect-error
  1065. compA.b = 'foo'
  1066. // @ts-expect-error
  1067. compA.c = 1
  1068. // @ts-expect-error
  1069. compA.mA = 'foo'
  1070. // @ts-expect-error
  1071. compA.baseA = 1
  1072. })
  1073. describe('async setup', () => {
  1074. type GT = string & { __brand: unknown }
  1075. const Comp = defineComponent({
  1076. async setup() {
  1077. // setup context
  1078. return {
  1079. a: ref(1),
  1080. b: {
  1081. c: ref('hi'),
  1082. },
  1083. d: reactive({
  1084. e: ref('hello' as GT),
  1085. }),
  1086. }
  1087. },
  1088. render() {
  1089. // assert setup context unwrapping
  1090. expectType<number>(this.a)
  1091. expectType<string>(this.b.c.value)
  1092. expectType<GT>(this.d.e)
  1093. // setup context properties should be mutable
  1094. this.a = 2
  1095. },
  1096. })
  1097. const vm = {} as InstanceType<typeof Comp>
  1098. // assert setup context unwrapping
  1099. expectType<number>(vm.a)
  1100. expectType<string>(vm.b.c.value)
  1101. expectType<GT>(vm.d.e)
  1102. // setup context properties should be mutable
  1103. vm.a = 2
  1104. })
  1105. // #5948
  1106. describe('DefineComponent should infer correct types when assigning to Component', () => {
  1107. let component: Component
  1108. component = defineComponent({
  1109. setup(_, { attrs, slots }) {
  1110. // @ts-expect-error should not be any
  1111. expectType<[]>(attrs)
  1112. // @ts-expect-error should not be any
  1113. expectType<[]>(slots)
  1114. },
  1115. })
  1116. expectType<Component>(component)
  1117. })
  1118. // #5969
  1119. describe('should allow to assign props', () => {
  1120. const Child = defineComponent({
  1121. props: {
  1122. bar: String,
  1123. },
  1124. })
  1125. const Parent = defineComponent({
  1126. props: {
  1127. ...Child.props,
  1128. foo: String,
  1129. },
  1130. })
  1131. const child = new Child()
  1132. expectType<JSX.Element>(<Parent {...child.$props} />)
  1133. })
  1134. // #6052
  1135. describe('prop starting with `on*` is broken', () => {
  1136. defineComponent({
  1137. props: {
  1138. onX: {
  1139. type: Function as PropType<(a: 1) => void>,
  1140. required: true,
  1141. },
  1142. },
  1143. setup(props) {
  1144. expectType<(a: 1) => void>(props.onX)
  1145. props.onX(1)
  1146. },
  1147. })
  1148. defineComponent({
  1149. props: {
  1150. onX: {
  1151. type: Function as PropType<(a: 1) => void>,
  1152. required: true,
  1153. },
  1154. },
  1155. emits: {
  1156. test: (a: 1) => true,
  1157. },
  1158. setup(props) {
  1159. expectType<(a: 1) => void>(props.onX)
  1160. expectType<undefined | ((a: 1) => any)>(props.onTest)
  1161. },
  1162. })
  1163. })
  1164. describe('function syntax w/ generics', () => {
  1165. const Comp = defineComponent(
  1166. // TODO: babel plugin to auto infer runtime props options from type
  1167. // similar to defineProps<{...}>()
  1168. <T extends string | number>(props: { msg: T; list: T[] }) => {
  1169. // use Composition API here like in <script setup>
  1170. const count = ref(0)
  1171. return () => (
  1172. // return a render function (both JSX and h() works)
  1173. <div>
  1174. {props.msg} {count.value}
  1175. </div>
  1176. )
  1177. },
  1178. )
  1179. expectType<JSX.Element>(<Comp msg="fse" list={['foo']} />)
  1180. expectType<JSX.Element>(<Comp msg={123} list={[123]} />)
  1181. expectType<JSX.Element>(
  1182. // @ts-expect-error missing prop
  1183. <Comp msg={123} />,
  1184. )
  1185. expectType<JSX.Element>(
  1186. // @ts-expect-error generics don't match
  1187. <Comp msg="fse" list={[123]} />,
  1188. )
  1189. expectType<JSX.Element>(
  1190. // @ts-expect-error generics don't match
  1191. <Comp msg={123} list={['123']} />,
  1192. )
  1193. })
  1194. describe('function syntax w/ emits', () => {
  1195. const Foo = defineComponent(
  1196. (props: { msg: string }, ctx) => {
  1197. ctx.emit('foo')
  1198. // @ts-expect-error
  1199. ctx.emit('bar')
  1200. return () => {}
  1201. },
  1202. {
  1203. emits: ['foo'],
  1204. },
  1205. )
  1206. expectType<JSX.Element>(<Foo msg="hi" onFoo={() => {}} />)
  1207. // @ts-expect-error
  1208. expectType<JSX.Element>(<Foo msg="hi" onBar={() => {}} />)
  1209. defineComponent(
  1210. (props: { msg: string }, ctx) => {
  1211. ctx.emit('foo', 'hi')
  1212. // @ts-expect-error
  1213. ctx.emit('foo')
  1214. // @ts-expect-error
  1215. ctx.emit('bar')
  1216. return () => {}
  1217. },
  1218. {
  1219. emits: {
  1220. foo: (a: string) => true,
  1221. },
  1222. },
  1223. )
  1224. })
  1225. describe('function syntax w/ runtime props', () => {
  1226. // with runtime props, the runtime props must match
  1227. // manual type declaration
  1228. defineComponent(
  1229. (_props: { msg: string }) => {
  1230. return () => {}
  1231. },
  1232. {
  1233. props: ['msg'],
  1234. },
  1235. )
  1236. defineComponent(
  1237. <T extends string>(_props: { msg: T }) => {
  1238. return () => {}
  1239. },
  1240. {
  1241. props: ['msg'],
  1242. },
  1243. )
  1244. defineComponent(
  1245. <T extends string>(_props: { msg: T }) => {
  1246. return () => {}
  1247. },
  1248. {
  1249. props: {
  1250. msg: String,
  1251. },
  1252. },
  1253. )
  1254. // @ts-expect-error string prop names don't match
  1255. defineComponent(
  1256. (_props: { msg: string }) => {
  1257. return () => {}
  1258. },
  1259. {
  1260. props: ['bar'],
  1261. },
  1262. )
  1263. defineComponent(
  1264. (_props: { msg: string }) => {
  1265. return () => {}
  1266. },
  1267. {
  1268. props: {
  1269. // @ts-expect-error prop type mismatch
  1270. msg: Number,
  1271. },
  1272. },
  1273. )
  1274. // @ts-expect-error prop keys don't match
  1275. defineComponent(
  1276. (_props: { msg: string }, ctx) => {
  1277. return () => {}
  1278. },
  1279. {
  1280. props: {
  1281. msg: String,
  1282. bar: String,
  1283. },
  1284. },
  1285. )
  1286. })
  1287. // check if defineComponent can be exported
  1288. export default {
  1289. // function components
  1290. a: defineComponent(_ => () => h('div')),
  1291. // no props
  1292. b: defineComponent({
  1293. data() {
  1294. return {}
  1295. },
  1296. }),
  1297. c: defineComponent({
  1298. props: ['a'],
  1299. }),
  1300. d: defineComponent({
  1301. props: {
  1302. a: Number,
  1303. },
  1304. }),
  1305. }
  1306. describe('slots', () => {
  1307. const comp1 = defineComponent({
  1308. slots: Object as SlotsType<{
  1309. default: { foo: string; bar: number }
  1310. optional?: { data: string }
  1311. undefinedScope: undefined | { data: string }
  1312. optionalUndefinedScope?: undefined | { data: string }
  1313. }>,
  1314. setup(props, { slots }) {
  1315. expectType<(scope: { foo: string; bar: number }) => VNode[]>(
  1316. slots.default,
  1317. )
  1318. expectType<((scope: { data: string }) => VNode[]) | undefined>(
  1319. slots.optional,
  1320. )
  1321. slots.default({ foo: 'foo', bar: 1 })
  1322. // @ts-expect-error it's optional
  1323. slots.optional({ data: 'foo' })
  1324. slots.optional?.({ data: 'foo' })
  1325. expectType<{
  1326. (): VNode[]
  1327. (scope: undefined | { data: string }): VNode[]
  1328. }>(slots.undefinedScope)
  1329. expectType<
  1330. | { (): VNode[]; (scope: undefined | { data: string }): VNode[] }
  1331. | undefined
  1332. >(slots.optionalUndefinedScope)
  1333. slots.default({ foo: 'foo', bar: 1 })
  1334. // @ts-expect-error it's optional
  1335. slots.optional({ data: 'foo' })
  1336. slots.optional?.({ data: 'foo' })
  1337. slots.undefinedScope()
  1338. slots.undefinedScope(undefined)
  1339. // @ts-expect-error
  1340. slots.undefinedScope('foo')
  1341. slots.optionalUndefinedScope?.()
  1342. slots.optionalUndefinedScope?.(undefined)
  1343. slots.optionalUndefinedScope?.({ data: 'foo' })
  1344. // @ts-expect-error
  1345. slots.optionalUndefinedScope()
  1346. // @ts-expect-error
  1347. slots.optionalUndefinedScope?.('foo')
  1348. expectType<typeof slots | undefined>(new comp1().$slots)
  1349. },
  1350. })
  1351. const comp2 = defineComponent({
  1352. setup(props, { slots }) {
  1353. // unknown slots
  1354. expectType<Slots>(slots)
  1355. expectType<((...args: any[]) => VNode[]) | undefined>(slots.default)
  1356. },
  1357. })
  1358. expectType<Slots | undefined>(new comp2().$slots)
  1359. })
  1360. // #5885
  1361. describe('should work when props type is incompatible with setup returned type ', () => {
  1362. type SizeType = 'small' | 'big'
  1363. const Comp = defineComponent({
  1364. props: {
  1365. size: {
  1366. type: String as PropType<SizeType>,
  1367. required: true,
  1368. },
  1369. },
  1370. setup(props) {
  1371. expectType<SizeType>(props.size)
  1372. return {
  1373. size: 1,
  1374. }
  1375. },
  1376. })
  1377. type CompInstance = InstanceType<typeof Comp>
  1378. const CompA = {} as CompInstance
  1379. expectType<ComponentPublicInstance>(CompA)
  1380. expectType<number>(CompA.size)
  1381. expectType<SizeType>(CompA.$props.size)
  1382. })
  1383. describe('withKeys and withModifiers as pro', () => {
  1384. const onKeydown = withKeys(e => {}, [''])
  1385. const onClick = withModifiers(e => {}, [])
  1386. ;<input onKeydown={onKeydown} onClick={onClick} />
  1387. })
  1388. import type {
  1389. AllowedComponentProps,
  1390. ComponentCustomProps,
  1391. ComponentOptionsMixin,
  1392. DefineComponent,
  1393. EmitsOptions,
  1394. ExtractPropTypes,
  1395. VNodeProps,
  1396. } from 'vue'
  1397. // code generated by tsc / vue-tsc, make sure this continues to work
  1398. // so we don't accidentally change the args order of DefineComponent
  1399. declare const MyButton: DefineComponent<
  1400. {},
  1401. () => JSX.Element,
  1402. {},
  1403. {},
  1404. {},
  1405. ComponentOptionsMixin,
  1406. ComponentOptionsMixin,
  1407. EmitsOptions,
  1408. string,
  1409. VNodeProps & AllowedComponentProps & ComponentCustomProps,
  1410. Readonly<ExtractPropTypes<{}>>,
  1411. {},
  1412. {}
  1413. >
  1414. ;<MyButton class="x" />