defineEmits.spec.ts 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. import { BindingTypes } from '@vue/compiler-core'
  2. import { assertCode, compileSFCScript as compile } from '../utils'
  3. describe('defineEmits', () => {
  4. test('basic usage', () => {
  5. const { content, bindings } = compile(`
  6. <script setup>
  7. const myEmit = defineEmits(['foo', 'bar'])
  8. </script>
  9. `)
  10. assertCode(content)
  11. expect(bindings).toStrictEqual({
  12. myEmit: BindingTypes.SETUP_CONST,
  13. })
  14. // should remove defineEmits import and call
  15. expect(content).not.toMatch('defineEmits')
  16. // should generate correct setup signature
  17. expect(content).toMatch(
  18. `setup(__props, { expose: __expose, emit: __emit }) {`,
  19. )
  20. expect(content).toMatch('const myEmit = __emit')
  21. // should include context options in default export
  22. expect(content).toMatch(`export default {
  23. emits: ['foo', 'bar'],`)
  24. })
  25. test('w/ runtime options', () => {
  26. const { content } = compile(`
  27. <script setup lang="ts">
  28. const emit = defineEmits(['a', 'b'])
  29. </script>
  30. `)
  31. assertCode(content)
  32. expect(content).toMatch(`export default /*@__PURE__*/_defineComponent({
  33. emits: ['a', 'b'],
  34. setup(__props, { expose: __expose, emit: __emit }) {`)
  35. expect(content).toMatch('const emit = __emit')
  36. })
  37. test('w/ type', () => {
  38. const { content } = compile(`
  39. <script setup lang="ts">
  40. const emit = defineEmits<(e: 'foo' | 'bar') => void>()
  41. </script>
  42. `)
  43. assertCode(content)
  44. expect(content).toMatch(`emits: ["foo", "bar"]`)
  45. })
  46. test('w/ type (union)', () => {
  47. const type = `((e: 'foo' | 'bar') => void) | ((e: 'baz', id: number) => void)`
  48. const { content } = compile(`
  49. <script setup lang="ts">
  50. const emit = defineEmits<${type}>()
  51. </script>
  52. `)
  53. assertCode(content)
  54. expect(content).toMatch(`emits: ["foo", "bar", "baz"]`)
  55. })
  56. test('w/ type (type literal w/ call signatures)', () => {
  57. const type = `{(e: 'foo' | 'bar'): void; (e: 'baz', id: number): void;}`
  58. const { content } = compile(`
  59. <script setup lang="ts">
  60. const emit = defineEmits<${type}>()
  61. </script>
  62. `)
  63. assertCode(content)
  64. expect(content).toMatch(`emits: ["foo", "bar", "baz"]`)
  65. })
  66. test('w/ type (interface)', () => {
  67. const { content } = compile(`
  68. <script setup lang="ts">
  69. interface Emits { (e: 'foo' | 'bar'): void }
  70. const emit = defineEmits<Emits>()
  71. </script>
  72. `)
  73. assertCode(content)
  74. expect(content).toMatch(`emits: ["foo", "bar"]`)
  75. })
  76. test('w/ type (interface w/ extends)', () => {
  77. const { content } = compile(`
  78. <script setup lang="ts">
  79. interface Base { (e: 'foo'): void }
  80. interface Emits extends Base { (e: 'bar'): void }
  81. const emit = defineEmits<Emits>()
  82. </script>
  83. `)
  84. assertCode(content)
  85. expect(content).toMatch(`emits: ["bar", "foo"]`)
  86. })
  87. test('w/ type (exported interface)', () => {
  88. const { content } = compile(`
  89. <script setup lang="ts">
  90. export interface Emits { (e: 'foo' | 'bar'): void }
  91. const emit = defineEmits<Emits>()
  92. </script>
  93. `)
  94. assertCode(content)
  95. expect(content).toMatch(`emits: ["foo", "bar"]`)
  96. })
  97. test('w/ type from normal script', () => {
  98. const { content } = compile(`
  99. <script lang="ts">
  100. export interface Emits { (e: 'foo' | 'bar'): void }
  101. </script>
  102. <script setup lang="ts">
  103. const emit = defineEmits<Emits>()
  104. </script>
  105. `)
  106. assertCode(content)
  107. expect(content).toMatch(`emits: ["foo", "bar"]`)
  108. })
  109. test('w/ type (type alias)', () => {
  110. const { content } = compile(`
  111. <script setup lang="ts">
  112. type Emits = { (e: 'foo' | 'bar'): void }
  113. const emit = defineEmits<Emits>()
  114. </script>
  115. `)
  116. assertCode(content)
  117. expect(content).toMatch(`emits: ["foo", "bar"]`)
  118. })
  119. test('w/ type (exported type alias)', () => {
  120. const { content } = compile(`
  121. <script setup lang="ts">
  122. export type Emits = { (e: 'foo' | 'bar'): void }
  123. const emit = defineEmits<Emits>()
  124. </script>
  125. `)
  126. assertCode(content)
  127. expect(content).toMatch(`emits: ["foo", "bar"]`)
  128. })
  129. test('w/ type (referenced function type)', () => {
  130. const { content } = compile(`
  131. <script setup lang="ts">
  132. type Emits = (e: 'foo' | 'bar') => void
  133. const emit = defineEmits<Emits>()
  134. </script>
  135. `)
  136. assertCode(content)
  137. expect(content).toMatch(`emits: ["foo", "bar"]`)
  138. })
  139. test('w/ type (referenced exported function type)', () => {
  140. const { content } = compile(`
  141. <script setup lang="ts">
  142. export type Emits = (e: 'foo' | 'bar') => void
  143. const emit = defineEmits<Emits>()
  144. </script>
  145. `)
  146. assertCode(content)
  147. expect(content).toMatch(`emits: ["foo", "bar"]`)
  148. })
  149. // #5393
  150. test('w/ type (interface ts type)', () => {
  151. const { content } = compile(`
  152. <script setup lang="ts">
  153. interface Emits { (e: 'foo'): void }
  154. const emit: Emits = defineEmits(['foo'])
  155. </script>
  156. `)
  157. assertCode(content)
  158. expect(content).toMatch(`emits: ['foo']`)
  159. })
  160. test('w/ type (property syntax)', () => {
  161. const { content } = compile(`
  162. <script setup lang="ts">
  163. const emit = defineEmits<{ foo: [], bar: [] }>()
  164. </script>
  165. `)
  166. expect(content).toMatch(`emits: ["foo", "bar"]`)
  167. assertCode(content)
  168. })
  169. // #8040
  170. test('w/ type (property syntax string literal)', () => {
  171. const { content } = compile(`
  172. <script setup lang="ts">
  173. const emit = defineEmits<{ 'foo:bar': [] }>()
  174. </script>
  175. `)
  176. expect(content).toMatch(`emits: ["foo:bar"]`)
  177. assertCode(content)
  178. })
  179. // #7943
  180. test('w/ type (type references in union)', () => {
  181. const { content } = compile(`
  182. <script setup lang="ts">
  183. type BaseEmit = "change"
  184. type Emit = "some" | "emit" | BaseEmit
  185. const emit = defineEmits<{
  186. (e: Emit): void;
  187. (e: "another", val: string): void;
  188. }>();
  189. </script>
  190. `)
  191. expect(content).toMatch(`emits: ["some", "emit", "change", "another"]`)
  192. assertCode(content)
  193. })
  194. describe('errors', () => {
  195. test('w/ both type and non-type args', () => {
  196. expect(() => {
  197. compile(`<script setup lang="ts">
  198. defineEmits<{}>({})
  199. </script>`)
  200. }).toThrow(`cannot accept both type and non-type arguments`)
  201. })
  202. test('mixed usage of property / call signature', () => {
  203. expect(() =>
  204. compile(`<script setup lang="ts">
  205. defineEmits<{
  206. foo: []
  207. (e: 'hi'): void
  208. }>()
  209. </script>`),
  210. ).toThrow(
  211. `defineEmits() type cannot mixed call signature and property syntax.`,
  212. )
  213. })
  214. })
  215. })