defineComponent.test-d.tsx 28 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144
  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. />
  273. )
  274. expectType<Component>(
  275. <MyComponent
  276. b="b"
  277. dd={{ n: 1 }}
  278. ddd={['ddd']}
  279. eee={() => ({ a: 'eee' })}
  280. fff={(a, b) => ({ a: a > +b })}
  281. hhh={false}
  282. jjj={() => ''}
  283. />
  284. )
  285. // @ts-expect-error missing required props
  286. expectError(<MyComponent />)
  287. expectError(
  288. // @ts-expect-error wrong prop types
  289. <MyComponent a={'wrong type'} b="foo" dd={{ n: 1 }} ddd={['foo']} />
  290. )
  291. expectError(
  292. // @ts-expect-error wrong prop types
  293. <MyComponent ggg="baz" />
  294. )
  295. // @ts-expect-error
  296. expectError(<MyComponent b="foo" dd={{ n: 'string' }} ddd={['foo']} />)
  297. // `this` should be void inside of prop validators and prop default factories
  298. defineComponent({
  299. props: {
  300. myProp: {
  301. type: Number,
  302. validator(val: unknown): boolean {
  303. // @ts-expect-error
  304. return val !== this.otherProp
  305. },
  306. default(): number {
  307. // @ts-expect-error
  308. return this.otherProp + 1
  309. }
  310. },
  311. otherProp: {
  312. type: Number,
  313. required: true
  314. }
  315. }
  316. })
  317. })
  318. describe('type inference w/ optional props declaration', () => {
  319. const MyComponent = defineComponent<{ a: string[]; msg: string }>({
  320. setup(props) {
  321. expectType<string>(props.msg)
  322. expectType<string[]>(props.a)
  323. return {
  324. b: 1
  325. }
  326. }
  327. })
  328. expectType<JSX.Element>(<MyComponent msg="1" a={['1']} />)
  329. // @ts-expect-error
  330. expectError(<MyComponent />)
  331. // @ts-expect-error
  332. expectError(<MyComponent msg="1" />)
  333. })
  334. describe('type inference w/ direct setup function', () => {
  335. const MyComponent = defineComponent((_props: { msg: string }) => {})
  336. expectType<JSX.Element>(<MyComponent msg="foo" />)
  337. // @ts-expect-error
  338. expectError(<MyComponent />)
  339. expectError(<MyComponent msg="1" />)
  340. })
  341. describe('type inference w/ array props declaration', () => {
  342. const MyComponent = defineComponent({
  343. props: ['a', 'b'],
  344. setup(props) {
  345. // @ts-expect-error props should be readonly
  346. expectError((props.a = 1))
  347. expectType<any>(props.a)
  348. expectType<any>(props.b)
  349. return {
  350. c: 1
  351. }
  352. },
  353. render() {
  354. expectType<any>(this.$props.a)
  355. expectType<any>(this.$props.b)
  356. // @ts-expect-error
  357. expectError((this.$props.a = 1))
  358. expectType<any>(this.a)
  359. expectType<any>(this.b)
  360. expectType<number>(this.c)
  361. }
  362. })
  363. expectType<JSX.Element>(<MyComponent a={[1, 2]} b="b" />)
  364. // @ts-expect-error
  365. expectError(<MyComponent other="other" />)
  366. })
  367. describe('type inference w/ options API', () => {
  368. defineComponent({
  369. props: { a: Number },
  370. setup() {
  371. return {
  372. b: 123
  373. }
  374. },
  375. data() {
  376. // Limitation: we cannot expose the return result of setup() on `this`
  377. // here in data() - somehow that would mess up the inference
  378. expectType<number | undefined>(this.a)
  379. return {
  380. c: this.a || 123,
  381. someRef: ref(0)
  382. }
  383. },
  384. computed: {
  385. d() {
  386. expectType<number>(this.b)
  387. return this.b + 1
  388. },
  389. e: {
  390. get() {
  391. expectType<number>(this.b)
  392. expectType<number>(this.d)
  393. return this.b + this.d
  394. },
  395. set(v: number) {
  396. expectType<number>(this.b)
  397. expectType<number>(this.d)
  398. expectType<number>(v)
  399. }
  400. }
  401. },
  402. watch: {
  403. a() {
  404. expectType<number>(this.b)
  405. this.b + 1
  406. }
  407. },
  408. created() {
  409. // props
  410. expectType<number | undefined>(this.a)
  411. // returned from setup()
  412. expectType<number>(this.b)
  413. // returned from data()
  414. expectType<number>(this.c)
  415. // computed
  416. expectType<number>(this.d)
  417. // computed get/set
  418. expectType<number>(this.e)
  419. expectType<number>(this.someRef)
  420. },
  421. methods: {
  422. doSomething() {
  423. // props
  424. expectType<number | undefined>(this.a)
  425. // returned from setup()
  426. expectType<number>(this.b)
  427. // returned from data()
  428. expectType<number>(this.c)
  429. // computed
  430. expectType<number>(this.d)
  431. // computed get/set
  432. expectType<number>(this.e)
  433. },
  434. returnSomething() {
  435. return this.a
  436. }
  437. },
  438. render() {
  439. // props
  440. expectType<number | undefined>(this.a)
  441. // returned from setup()
  442. expectType<number>(this.b)
  443. // returned from data()
  444. expectType<number>(this.c)
  445. // computed
  446. expectType<number>(this.d)
  447. // computed get/set
  448. expectType<number>(this.e)
  449. // method
  450. expectType<() => number | undefined>(this.returnSomething)
  451. }
  452. })
  453. })
  454. describe('with mixins', () => {
  455. const MixinA = defineComponent({
  456. emits: ['bar'],
  457. props: {
  458. aP1: {
  459. type: String,
  460. default: 'aP1'
  461. },
  462. aP2: Boolean
  463. },
  464. data() {
  465. return {
  466. a: 1
  467. }
  468. }
  469. })
  470. const MixinB = defineComponent({
  471. props: ['bP1', 'bP2'],
  472. data() {
  473. return {
  474. b: 2
  475. }
  476. }
  477. })
  478. const MixinC = defineComponent({
  479. data() {
  480. return {
  481. c: 3
  482. }
  483. }
  484. })
  485. const MixinD = defineComponent({
  486. mixins: [MixinA],
  487. data() {
  488. //@ts-expect-error computed are not available on data()
  489. expectError<number>(this.dC1)
  490. //@ts-expect-error computed are not available on data()
  491. expectError<string>(this.dC2)
  492. return {
  493. d: 4
  494. }
  495. },
  496. setup(props) {
  497. expectType<string>(props.aP1)
  498. },
  499. computed: {
  500. dC1() {
  501. return this.d + this.a
  502. },
  503. dC2() {
  504. return this.aP1 + 'dC2'
  505. }
  506. }
  507. })
  508. const MyComponent = defineComponent({
  509. mixins: [MixinA, MixinB, MixinC, MixinD],
  510. emits: ['click'],
  511. props: {
  512. // required should make property non-void
  513. z: {
  514. type: String,
  515. required: true
  516. }
  517. },
  518. data(vm) {
  519. expectType<number>(vm.a)
  520. expectType<number>(vm.b)
  521. expectType<number>(vm.c)
  522. expectType<number>(vm.d)
  523. // should also expose declared props on `this`
  524. expectType<number>(this.a)
  525. expectType<string>(this.aP1)
  526. expectType<boolean | undefined>(this.aP2)
  527. expectType<number>(this.b)
  528. expectType<any>(this.bP1)
  529. expectType<number>(this.c)
  530. expectType<number>(this.d)
  531. return {}
  532. },
  533. setup(props) {
  534. expectType<string>(props.z)
  535. // props
  536. expectType<((...args: any[]) => any) | undefined>(props.onClick)
  537. // from Base
  538. expectType<((...args: any[]) => any) | undefined>(props.onBar)
  539. expectType<string>(props.aP1)
  540. expectType<boolean | undefined>(props.aP2)
  541. expectType<any>(props.bP1)
  542. expectType<any>(props.bP2)
  543. expectType<string>(props.z)
  544. },
  545. render() {
  546. const props = this.$props
  547. // props
  548. expectType<((...args: any[]) => any) | undefined>(props.onClick)
  549. // from Base
  550. expectType<((...args: any[]) => any) | undefined>(props.onBar)
  551. expectType<string>(props.aP1)
  552. expectType<boolean | undefined>(props.aP2)
  553. expectType<any>(props.bP1)
  554. expectType<any>(props.bP2)
  555. expectType<string>(props.z)
  556. const data = this.$data
  557. expectType<number>(data.a)
  558. expectType<number>(data.b)
  559. expectType<number>(data.c)
  560. expectType<number>(data.d)
  561. // should also expose declared props on `this`
  562. expectType<number>(this.a)
  563. expectType<string>(this.aP1)
  564. expectType<boolean | undefined>(this.aP2)
  565. expectType<number>(this.b)
  566. expectType<any>(this.bP1)
  567. expectType<number>(this.c)
  568. expectType<number>(this.d)
  569. expectType<number>(this.dC1)
  570. expectType<string>(this.dC2)
  571. // props should be readonly
  572. // @ts-expect-error
  573. expectError((this.aP1 = 'new'))
  574. // @ts-expect-error
  575. expectError((this.z = 1))
  576. // props on `this` should be readonly
  577. // @ts-expect-error
  578. expectError((this.bP1 = 1))
  579. // string value can not assigned to number type value
  580. // @ts-expect-error
  581. expectError((this.c = '1'))
  582. // setup context properties should be mutable
  583. this.d = 5
  584. return null
  585. }
  586. })
  587. // Test TSX
  588. expectType<JSX.Element>(
  589. <MyComponent aP1={'aP'} aP2 bP1={1} bP2={[1, 2]} z={'z'} />
  590. )
  591. // missing required props
  592. // @ts-expect-error
  593. expectError(<MyComponent />)
  594. // wrong prop types
  595. // @ts-expect-error
  596. expectError(<MyComponent aP1="ap" aP2={'wrong type'} bP1="b" z={'z'} />)
  597. // @ts-expect-error
  598. expectError(<MyComponent aP1={1} bP2={[1]} />)
  599. })
  600. describe('with extends', () => {
  601. const Base = defineComponent({
  602. props: {
  603. aP1: Boolean,
  604. aP2: {
  605. type: Number,
  606. default: 2
  607. }
  608. },
  609. data() {
  610. return {
  611. a: 1
  612. }
  613. },
  614. computed: {
  615. c(): number {
  616. return this.aP2 + this.a
  617. }
  618. }
  619. })
  620. const MyComponent = defineComponent({
  621. extends: Base,
  622. props: {
  623. // required should make property non-void
  624. z: {
  625. type: String,
  626. required: true
  627. }
  628. },
  629. render() {
  630. const props = this.$props
  631. // props
  632. expectType<boolean | undefined>(props.aP1)
  633. expectType<number>(props.aP2)
  634. expectType<string>(props.z)
  635. const data = this.$data
  636. expectType<number>(data.a)
  637. // should also expose declared props on `this`
  638. expectType<number>(this.a)
  639. expectType<boolean | undefined>(this.aP1)
  640. expectType<number>(this.aP2)
  641. // setup context properties should be mutable
  642. this.a = 5
  643. return null
  644. }
  645. })
  646. // Test TSX
  647. expectType<JSX.Element>(<MyComponent aP2={3} aP1 z={'z'} />)
  648. // missing required props
  649. // @ts-expect-error
  650. expectError(<MyComponent />)
  651. // wrong prop types
  652. // @ts-expect-error
  653. expectError(<MyComponent aP2={'wrong type'} z={'z'} />)
  654. // @ts-expect-error
  655. expectError(<MyComponent aP1={3} />)
  656. })
  657. describe('extends with mixins', () => {
  658. const Mixin = defineComponent({
  659. emits: ['bar'],
  660. props: {
  661. mP1: {
  662. type: String,
  663. default: 'mP1'
  664. },
  665. mP2: Boolean,
  666. mP3: {
  667. type: Boolean,
  668. required: true
  669. }
  670. },
  671. data() {
  672. return {
  673. a: 1
  674. }
  675. }
  676. })
  677. const Base = defineComponent({
  678. emits: ['foo'],
  679. props: {
  680. p1: Boolean,
  681. p2: {
  682. type: Number,
  683. default: 2
  684. },
  685. p3: {
  686. type: Boolean,
  687. required: true
  688. }
  689. },
  690. data() {
  691. return {
  692. b: 2
  693. }
  694. },
  695. computed: {
  696. c(): number {
  697. return this.p2 + this.b
  698. }
  699. }
  700. })
  701. const MyComponent = defineComponent({
  702. extends: Base,
  703. mixins: [Mixin],
  704. emits: ['click'],
  705. props: {
  706. // required should make property non-void
  707. z: {
  708. type: String,
  709. required: true
  710. }
  711. },
  712. render() {
  713. const props = this.$props
  714. // props
  715. expectType<((...args: any[]) => any) | undefined>(props.onClick)
  716. // from Mixin
  717. expectType<((...args: any[]) => any) | undefined>(props.onBar)
  718. // from Base
  719. expectType<((...args: any[]) => any) | undefined>(props.onFoo)
  720. expectType<boolean | undefined>(props.p1)
  721. expectType<number>(props.p2)
  722. expectType<string>(props.z)
  723. expectType<string>(props.mP1)
  724. expectType<boolean | undefined>(props.mP2)
  725. const data = this.$data
  726. expectType<number>(data.a)
  727. expectType<number>(data.b)
  728. // should also expose declared props on `this`
  729. expectType<number>(this.a)
  730. expectType<number>(this.b)
  731. expectType<boolean | undefined>(this.p1)
  732. expectType<number>(this.p2)
  733. expectType<string>(this.mP1)
  734. expectType<boolean | undefined>(this.mP2)
  735. // setup context properties should be mutable
  736. this.a = 5
  737. return null
  738. }
  739. })
  740. // Test TSX
  741. expectType<JSX.Element>(<MyComponent mP1="p1" mP2 mP3 p1 p2={1} p3 z={'z'} />)
  742. // mP1, mP2, p1, and p2 have default value. these are not required
  743. expectType<JSX.Element>(<MyComponent mP3 p3 z={'z'} />)
  744. // missing required props
  745. // @ts-expect-error
  746. expectError(<MyComponent mP3 p3 /* z='z' */ />)
  747. // missing required props from mixin
  748. // @ts-expect-error
  749. expectError(<MyComponent /* mP3 */ p3 z="z" />)
  750. // missing required props from extends
  751. // @ts-expect-error
  752. expectError(<MyComponent mP3 /* p3 */ z="z" />)
  753. // wrong prop types
  754. // @ts-expect-error
  755. expectError(<MyComponent p2={'wrong type'} z={'z'} />)
  756. // @ts-expect-error
  757. expectError(<MyComponent mP1={3} />)
  758. // #3468
  759. const CompWithD = defineComponent({
  760. data() {
  761. return { foo: 1 }
  762. }
  763. })
  764. const CompWithC = defineComponent({
  765. computed: {
  766. foo() {
  767. return 1
  768. }
  769. }
  770. })
  771. const CompWithM = defineComponent({ methods: { foo() {} } })
  772. const CompEmpty = defineComponent({})
  773. defineComponent({
  774. mixins: [CompWithD, CompEmpty],
  775. mounted() {
  776. expectType<number>(this.foo)
  777. }
  778. })
  779. defineComponent({
  780. mixins: [CompWithC, CompEmpty],
  781. mounted() {
  782. expectType<number>(this.foo)
  783. }
  784. })
  785. defineComponent({
  786. mixins: [CompWithM, CompEmpty],
  787. mounted() {
  788. expectType<() => void>(this.foo)
  789. }
  790. })
  791. })
  792. describe('compatibility w/ createApp', () => {
  793. const comp = defineComponent({})
  794. createApp(comp).mount('#hello')
  795. const comp2 = defineComponent({
  796. props: { foo: String }
  797. })
  798. createApp(comp2).mount('#hello')
  799. const comp3 = defineComponent({
  800. setup() {
  801. return {
  802. a: 1
  803. }
  804. }
  805. })
  806. createApp(comp3).mount('#hello')
  807. })
  808. describe('defineComponent', () => {
  809. test('should accept components defined with defineComponent', () => {
  810. const comp = defineComponent({})
  811. defineComponent({
  812. components: { comp }
  813. })
  814. })
  815. test('should accept class components with receiving constructor arguments', () => {
  816. class Comp {
  817. static __vccOpts = {}
  818. constructor(_props: { foo: string }) {}
  819. }
  820. defineComponent({
  821. components: { Comp }
  822. })
  823. })
  824. })
  825. describe('emits', () => {
  826. // Note: for TSX inference, ideally we want to map emits to onXXX props,
  827. // but that requires type-level string constant concatenation as suggested in
  828. // https://github.com/Microsoft/TypeScript/issues/12754
  829. // The workaround for TSX users is instead of using emits, declare onXXX props
  830. // and call them instead. Since `v-on:click` compiles to an `onClick` prop,
  831. // this would also support other users consuming the component in templates
  832. // with `v-on` listeners.
  833. // with object emits
  834. defineComponent({
  835. emits: {
  836. click: (n: number) => typeof n === 'number',
  837. input: (b: string) => b.length > 1
  838. },
  839. setup(props, { emit }) {
  840. expectType<((n: number) => boolean) | undefined>(props.onClick)
  841. expectType<((b: string) => boolean) | undefined>(props.onInput)
  842. emit('click', 1)
  843. emit('input', 'foo')
  844. // @ts-expect-error
  845. expectError(emit('nope'))
  846. // @ts-expect-error
  847. expectError(emit('click'))
  848. // @ts-expect-error
  849. expectError(emit('click', 'foo'))
  850. // @ts-expect-error
  851. expectError(emit('input'))
  852. // @ts-expect-error
  853. expectError(emit('input', 1))
  854. },
  855. created() {
  856. this.$emit('click', 1)
  857. this.$emit('input', 'foo')
  858. // @ts-expect-error
  859. expectError(this.$emit('nope'))
  860. // @ts-expect-error
  861. expectError(this.$emit('click'))
  862. // @ts-expect-error
  863. expectError(this.$emit('click', 'foo'))
  864. // @ts-expect-error
  865. expectError(this.$emit('input'))
  866. // @ts-expect-error
  867. expectError(this.$emit('input', 1))
  868. },
  869. mounted() {
  870. // #3599
  871. this.$nextTick(function () {
  872. // this should be bound to this instance
  873. this.$emit('click', 1)
  874. this.$emit('input', 'foo')
  875. // @ts-expect-error
  876. expectError(this.$emit('nope'))
  877. // @ts-expect-error
  878. expectError(this.$emit('click'))
  879. // @ts-expect-error
  880. expectError(this.$emit('click', 'foo'))
  881. // @ts-expect-error
  882. expectError(this.$emit('input'))
  883. // @ts-expect-error
  884. expectError(this.$emit('input', 1))
  885. })
  886. }
  887. })
  888. // with array emits
  889. defineComponent({
  890. emits: ['foo', 'bar'],
  891. setup(props, { emit }) {
  892. expectType<((...args: any[]) => any) | undefined>(props.onFoo)
  893. expectType<((...args: any[]) => any) | undefined>(props.onBar)
  894. emit('foo')
  895. emit('foo', 123)
  896. emit('bar')
  897. // @ts-expect-error
  898. expectError(emit('nope'))
  899. },
  900. created() {
  901. this.$emit('foo')
  902. this.$emit('foo', 123)
  903. this.$emit('bar')
  904. // @ts-expect-error
  905. expectError(this.$emit('nope'))
  906. }
  907. })
  908. // with tsx
  909. const Component = defineComponent({
  910. emits: {
  911. click: (n: number) => typeof n === 'number'
  912. },
  913. setup(props, { emit }) {
  914. expectType<((n: number) => any) | undefined>(props.onClick)
  915. emit('click', 1)
  916. // @ts-expect-error
  917. expectError(emit('click'))
  918. // @ts-expect-error
  919. expectError(emit('click', 'foo'))
  920. }
  921. })
  922. defineComponent({
  923. render() {
  924. return (
  925. <Component
  926. onClick={(n: number) => {
  927. return n + 1
  928. }}
  929. />
  930. )
  931. }
  932. })
  933. // without emits
  934. defineComponent({
  935. setup(props, { emit }) {
  936. emit('test', 1)
  937. emit('test')
  938. }
  939. })
  940. // emit should be valid when ComponentPublicInstance is used.
  941. const instance = {} as ComponentPublicInstance
  942. instance.$emit('test', 1)
  943. instance.$emit('test')
  944. // `this` should be void inside of emits validators
  945. defineComponent({
  946. props: ['bar'],
  947. emits: {
  948. foo(): boolean {
  949. // @ts-expect-error
  950. return this.bar === 3
  951. }
  952. }
  953. })
  954. })
  955. describe('componentOptions setup should be `SetupContext`', () => {
  956. expect<ComponentOptions['setup']>(
  957. {} as (props: Record<string, any>, ctx: SetupContext) => any
  958. )
  959. })
  960. describe('extract instance type', () => {
  961. const Base = defineComponent({
  962. props: {
  963. baseA: {
  964. type: Number,
  965. default: 1
  966. }
  967. }
  968. })
  969. const MixinA = defineComponent({
  970. props: {
  971. mA: {
  972. type: String,
  973. default: ''
  974. }
  975. }
  976. })
  977. const CompA = defineComponent({
  978. extends: Base,
  979. mixins: [MixinA],
  980. props: {
  981. a: {
  982. type: Boolean,
  983. default: false
  984. },
  985. b: {
  986. type: String,
  987. required: true
  988. },
  989. c: Number
  990. }
  991. })
  992. const compA = {} as InstanceType<typeof CompA>
  993. expectType<boolean>(compA.a)
  994. expectType<string>(compA.b)
  995. expectType<number | undefined>(compA.c)
  996. // mixins
  997. expectType<string>(compA.mA)
  998. // extends
  999. expectType<number>(compA.baseA)
  1000. // @ts-expect-error
  1001. expectError((compA.a = true))
  1002. // @ts-expect-error
  1003. expectError((compA.b = 'foo'))
  1004. // @ts-expect-error
  1005. expectError((compA.c = 1))
  1006. // @ts-expect-error
  1007. expectError((compA.mA = 'foo'))
  1008. // @ts-expect-error
  1009. expectError((compA.baseA = 1))
  1010. })
  1011. describe('async setup', () => {
  1012. type GT = string & { __brand: unknown }
  1013. const Comp = defineComponent({
  1014. async setup() {
  1015. // setup context
  1016. return {
  1017. a: ref(1),
  1018. b: {
  1019. c: ref('hi')
  1020. },
  1021. d: reactive({
  1022. e: ref('hello' as GT)
  1023. })
  1024. }
  1025. },
  1026. render() {
  1027. // assert setup context unwrapping
  1028. expectType<number>(this.a)
  1029. expectType<string>(this.b.c.value)
  1030. expectType<GT>(this.d.e)
  1031. // setup context properties should be mutable
  1032. this.a = 2
  1033. }
  1034. })
  1035. const vm = {} as InstanceType<typeof Comp>
  1036. // assert setup context unwrapping
  1037. expectType<number>(vm.a)
  1038. expectType<string>(vm.b.c.value)
  1039. expectType<GT>(vm.d.e)
  1040. // setup context properties should be mutable
  1041. vm.a = 2
  1042. })
  1043. // check if defineComponent can be exported
  1044. export default {
  1045. // function components
  1046. a: defineComponent(_ => h('div')),
  1047. // no props
  1048. b: defineComponent({
  1049. data() {
  1050. return {}
  1051. }
  1052. }),
  1053. c: defineComponent({
  1054. props: ['a']
  1055. }),
  1056. d: defineComponent({
  1057. props: {
  1058. a: Number
  1059. }
  1060. })
  1061. }