defineEmits.spec.ts 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. import { BindingTypes } from '@vue/compiler-core'
  2. import { compileSFCScript as compile, assertCode } 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 (exported interface)', () => {
  77. const { content } = compile(`
  78. <script setup lang="ts">
  79. export interface Emits { (e: 'foo' | 'bar'): void }
  80. const emit = defineEmits<Emits>()
  81. </script>
  82. `)
  83. assertCode(content)
  84. expect(content).toMatch(`emits: ["foo", "bar"]`)
  85. })
  86. test('w/ type from normal script', () => {
  87. const { content } = compile(`
  88. <script lang="ts">
  89. export interface Emits { (e: 'foo' | 'bar'): void }
  90. </script>
  91. <script setup lang="ts">
  92. const emit = defineEmits<Emits>()
  93. </script>
  94. `)
  95. assertCode(content)
  96. expect(content).toMatch(`emits: ["foo", "bar"]`)
  97. })
  98. test('w/ type (type alias)', () => {
  99. const { content } = compile(`
  100. <script setup lang="ts">
  101. type Emits = { (e: 'foo' | 'bar'): void }
  102. const emit = defineEmits<Emits>()
  103. </script>
  104. `)
  105. assertCode(content)
  106. expect(content).toMatch(`emits: ["foo", "bar"]`)
  107. })
  108. test('w/ type (exported type alias)', () => {
  109. const { content } = compile(`
  110. <script setup lang="ts">
  111. export type Emits = { (e: 'foo' | 'bar'): void }
  112. const emit = defineEmits<Emits>()
  113. </script>
  114. `)
  115. assertCode(content)
  116. expect(content).toMatch(`emits: ["foo", "bar"]`)
  117. })
  118. test('w/ type (referenced function type)', () => {
  119. const { content } = compile(`
  120. <script setup lang="ts">
  121. type Emits = (e: 'foo' | 'bar') => void
  122. const emit = defineEmits<Emits>()
  123. </script>
  124. `)
  125. assertCode(content)
  126. expect(content).toMatch(`emits: ["foo", "bar"]`)
  127. })
  128. test('w/ type (referenced exported function type)', () => {
  129. const { content } = compile(`
  130. <script setup lang="ts">
  131. export type Emits = (e: 'foo' | 'bar') => void
  132. const emit = defineEmits<Emits>()
  133. </script>
  134. `)
  135. assertCode(content)
  136. expect(content).toMatch(`emits: ["foo", "bar"]`)
  137. })
  138. // #5393
  139. test('w/ type (interface ts type)', () => {
  140. const { content } = compile(`
  141. <script setup lang="ts">
  142. interface Emits { (e: 'foo'): void }
  143. const emit: Emits = defineEmits(['foo'])
  144. </script>
  145. `)
  146. assertCode(content)
  147. expect(content).toMatch(`emits: ['foo']`)
  148. })
  149. test('w/ type (property syntax)', () => {
  150. const { content } = compile(`
  151. <script setup lang="ts">
  152. const emit = defineEmits<{ foo: [], bar: [] }>()
  153. </script>
  154. `)
  155. expect(content).toMatch(`emits: ["foo", "bar"]`)
  156. assertCode(content)
  157. })
  158. // #8040
  159. test('w/ type (property syntax string literal)', () => {
  160. const { content } = compile(`
  161. <script setup lang="ts">
  162. const emit = defineEmits<{ 'foo:bar': [] }>()
  163. </script>
  164. `)
  165. expect(content).toMatch(`emits: ["foo:bar"]`)
  166. assertCode(content)
  167. })
  168. // #7943
  169. test('w/ type (type references in union)', () => {
  170. const { content } = compile(`
  171. <script setup lang="ts">
  172. type BaseEmit = "change"
  173. type Emit = "some" | "emit" | BaseEmit
  174. const emit = defineEmits<{
  175. (e: Emit): void;
  176. (e: "another", val: string): void;
  177. }>();
  178. </script>
  179. `)
  180. expect(content).toMatch(`emits: ["some", "emit", "change", "another"]`)
  181. assertCode(content)
  182. })
  183. describe('errors', () => {
  184. test('w/ both type and non-type args', () => {
  185. expect(() => {
  186. compile(`<script setup lang="ts">
  187. defineEmits<{}>({})
  188. </script>`)
  189. }).toThrow(`cannot accept both type and non-type arguments`)
  190. })
  191. test('mixed usage of property / call signature', () => {
  192. expect(() =>
  193. compile(`<script setup lang="ts">
  194. defineEmits<{
  195. foo: []
  196. (e: 'hi'): void
  197. }>()
  198. </script>`)
  199. ).toThrow(
  200. `defineEmits() type cannot mixed call signature and property syntax.`
  201. )
  202. })
  203. })
  204. })