setupHelpers.test-d.ts 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. import {
  2. defineProps,
  3. defineEmits,
  4. useAttrs,
  5. useSlots,
  6. withDefaults,
  7. Slots,
  8. defineSlots,
  9. VNode,
  10. Ref,
  11. defineModel,
  12. toRefs
  13. } from 'vue'
  14. import { describe, expectType } from './utils'
  15. import { defineComponent } from 'vue'
  16. import { useModel } from 'vue'
  17. describe('defineProps w/ type declaration', () => {
  18. // type declaration
  19. const props = defineProps<{
  20. foo: string
  21. bool?: boolean
  22. boolAndUndefined: boolean | undefined
  23. file?: File | File[]
  24. }>()
  25. // explicitly declared type should be refined
  26. expectType<string>(props.foo)
  27. // @ts-expect-error
  28. props.bar
  29. expectType<boolean>(props.bool)
  30. expectType<boolean>(props.boolAndUndefined)
  31. })
  32. describe('defineProps w/ generics', () => {
  33. function test<T extends boolean>() {
  34. const props = defineProps<{ foo: T; bar: string; x?: boolean }>()
  35. expectType<T>(props.foo)
  36. expectType<string>(props.bar)
  37. expectType<boolean>(props.x)
  38. }
  39. test()
  40. })
  41. describe('defineProps w/ type declaration + withDefaults', () => {
  42. const res = withDefaults(
  43. defineProps<{
  44. number?: number
  45. arr?: string[]
  46. obj?: { x: number }
  47. fn?: (e: string) => void
  48. genStr?: string
  49. x?: string
  50. y?: string
  51. z?: string
  52. bool?: boolean
  53. boolAndUndefined: boolean | undefined
  54. }>(),
  55. {
  56. number: 123,
  57. arr: () => [],
  58. obj: () => ({ x: 123 }),
  59. fn: () => {},
  60. genStr: () => '',
  61. y: undefined,
  62. z: 'string'
  63. }
  64. )
  65. res.number + 1
  66. res.arr.push('hi')
  67. res.obj.x
  68. res.fn('hi')
  69. res.genStr.slice()
  70. // @ts-expect-error
  71. res.x.slice()
  72. // @ts-expect-error
  73. res.y.slice()
  74. expectType<string | undefined>(res.x)
  75. expectType<string | undefined>(res.y)
  76. expectType<string>(res.z)
  77. expectType<boolean>(res.bool)
  78. expectType<boolean>(res.boolAndUndefined)
  79. })
  80. describe('defineProps w/ union type declaration + withDefaults', () => {
  81. withDefaults(
  82. defineProps<{
  83. union1?: number | number[] | { x: number }
  84. union2?: number | number[] | { x: number }
  85. union3?: number | number[] | { x: number }
  86. union4?: number | number[] | { x: number }
  87. }>(),
  88. {
  89. union1: 123,
  90. union2: () => [123],
  91. union3: () => ({ x: 123 }),
  92. union4: () => 123
  93. }
  94. )
  95. })
  96. describe('defineProps w/ generic type declaration + withDefaults', <T extends
  97. number, TA extends {
  98. a: string
  99. }, TString extends string>() => {
  100. const res = withDefaults(
  101. defineProps<{
  102. n?: number
  103. bool?: boolean
  104. generic1?: T[] | { x: T }
  105. generic2?: { x: T }
  106. generic3?: TString
  107. generic4?: TA
  108. }>(),
  109. {
  110. n: 123,
  111. generic1: () => [123, 33] as T[],
  112. generic2: () => ({ x: 123 }) as { x: T },
  113. generic3: () => 'test' as TString,
  114. generic4: () => ({ a: 'test' }) as TA
  115. }
  116. )
  117. res.n + 1
  118. expectType<T[] | { x: T }>(res.generic1)
  119. expectType<{ x: T }>(res.generic2)
  120. expectType<TString>(res.generic3)
  121. expectType<TA>(res.generic4)
  122. expectType<boolean>(res.bool)
  123. })
  124. describe('withDefaults w/ boolean type', () => {
  125. const res1 = withDefaults(
  126. defineProps<{
  127. bool?: boolean
  128. }>(),
  129. { bool: false }
  130. )
  131. expectType<boolean>(res1.bool)
  132. const res2 = withDefaults(
  133. defineProps<{
  134. bool?: boolean
  135. }>(),
  136. {
  137. bool: undefined
  138. }
  139. )
  140. expectType<boolean | undefined>(res2.bool)
  141. })
  142. describe('defineProps w/ runtime declaration', () => {
  143. // runtime declaration
  144. const props = defineProps({
  145. foo: String,
  146. bar: {
  147. type: Number,
  148. default: 1
  149. },
  150. baz: {
  151. type: Array,
  152. required: true
  153. }
  154. })
  155. expectType<{
  156. foo?: string
  157. bar: number
  158. baz: unknown[]
  159. }>(props)
  160. props.foo && props.foo + 'bar'
  161. props.bar + 1
  162. // @ts-expect-error should be readonly
  163. props.bar++
  164. props.baz.push(1)
  165. const props2 = defineProps(['foo', 'bar'])
  166. props2.foo + props2.bar
  167. // @ts-expect-error
  168. props2.baz
  169. })
  170. describe('defineEmits w/ type declaration', () => {
  171. const emit = defineEmits<(e: 'change') => void>()
  172. emit('change')
  173. // @ts-expect-error
  174. emit()
  175. // @ts-expect-error
  176. emit('bar')
  177. type Emits = { (e: 'foo' | 'bar'): void; (e: 'baz', id: number): void }
  178. const emit2 = defineEmits<Emits>()
  179. emit2('foo')
  180. emit2('bar')
  181. emit2('baz', 123)
  182. // @ts-expect-error
  183. emit2('baz')
  184. })
  185. describe('defineEmits w/ alt type declaration', () => {
  186. const emit = defineEmits<{
  187. foo: [id: string]
  188. bar: any[]
  189. baz: []
  190. }>()
  191. emit('foo', 'hi')
  192. // @ts-expect-error
  193. emit('foo')
  194. emit('bar')
  195. emit('bar', 1, 2, 3)
  196. emit('baz')
  197. // @ts-expect-error
  198. emit('baz', 1)
  199. })
  200. describe('defineEmits w/ runtime declaration', () => {
  201. const emit = defineEmits({
  202. foo: () => {},
  203. bar: null
  204. })
  205. emit('foo')
  206. emit('bar', 123)
  207. // @ts-expect-error
  208. emit('baz')
  209. const emit2 = defineEmits(['foo', 'bar'])
  210. emit2('foo')
  211. emit2('bar', 123)
  212. // @ts-expect-error
  213. emit2('baz')
  214. })
  215. describe('defineSlots', () => {
  216. // literal fn syntax (allow for specifying return type)
  217. const fnSlots = defineSlots<{
  218. default(props: { foo: string; bar: number }): any
  219. optional?(props: string): any
  220. }>()
  221. expectType<(scope: { foo: string; bar: number }) => VNode[]>(fnSlots.default)
  222. expectType<undefined | ((scope: string) => VNode[])>(fnSlots.optional)
  223. const slotsUntype = defineSlots()
  224. expectType<Slots>(slotsUntype)
  225. })
  226. describe('defineModel', () => {
  227. // overload 1
  228. const modelValueRequired = defineModel<boolean>({ required: true })
  229. expectType<Ref<boolean>>(modelValueRequired)
  230. // overload 2
  231. const modelValue = defineModel<string>()
  232. expectType<Ref<string | undefined>>(modelValue)
  233. modelValue.value = 'new value'
  234. const modelValueDefault = defineModel<boolean>({ default: true })
  235. expectType<Ref<boolean>>(modelValueDefault)
  236. // overload 3
  237. const countRequired = defineModel<number>('count', { required: false })
  238. expectType<Ref<number | undefined>>(countRequired)
  239. // overload 4
  240. const count = defineModel<number>('count')
  241. expectType<Ref<number | undefined>>(count)
  242. const countDefault = defineModel<number>('count', { default: 1 })
  243. expectType<Ref<number>>(countDefault)
  244. // infer type from default
  245. const inferred = defineModel({ default: 123 })
  246. expectType<Ref<number | undefined>>(inferred)
  247. const inferredRequired = defineModel({ default: 123, required: true })
  248. expectType<Ref<number>>(inferredRequired)
  249. // @ts-expect-error type / default mismatch
  250. defineModel<string>({ default: 123 })
  251. // @ts-expect-error unknown props option
  252. defineModel({ foo: 123 })
  253. // accept defineModel-only options
  254. defineModel({ local: true })
  255. defineModel('foo', { local: true })
  256. })
  257. describe('useModel', () => {
  258. defineComponent({
  259. props: ['foo'],
  260. setup(props) {
  261. const r = useModel(props, 'foo')
  262. expectType<Ref<any>>(r)
  263. // @ts-expect-error
  264. useModel(props, 'bar')
  265. }
  266. })
  267. defineComponent({
  268. props: {
  269. foo: String,
  270. bar: { type: Number, required: true },
  271. baz: { type: Boolean }
  272. },
  273. setup(props) {
  274. expectType<Ref<string | undefined>>(useModel(props, 'foo'))
  275. expectType<Ref<number>>(useModel(props, 'bar'))
  276. expectType<Ref<boolean>>(useModel(props, 'baz'))
  277. }
  278. })
  279. })
  280. describe('useAttrs', () => {
  281. const attrs = useAttrs()
  282. expectType<Record<string, unknown>>(attrs)
  283. })
  284. describe('useSlots', () => {
  285. const slots = useSlots()
  286. expectType<Slots>(slots)
  287. })
  288. // #6420
  289. describe('toRefs w/ type declaration', () => {
  290. const props = defineProps<{
  291. file?: File | File[]
  292. }>()
  293. expectType<Ref<File | File[] | undefined>>(toRefs(props).file)
  294. })