ref.test-d.ts 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  1. import {
  2. type ComputedRef,
  3. type MaybeRef,
  4. type MaybeRefOrGetter,
  5. type Ref,
  6. type ShallowRef,
  7. type TemplateRef,
  8. type ToRefs,
  9. type WritableComputedRef,
  10. computed,
  11. isRef,
  12. proxyRefs,
  13. reactive,
  14. readonly,
  15. ref,
  16. shallowReactive,
  17. shallowRef,
  18. toRef,
  19. toRefs,
  20. toValue,
  21. unref,
  22. useTemplateRef,
  23. } from 'vue'
  24. import { type IsAny, type IsUnion, describe, expectType } from './utils'
  25. function plainType(arg: number | Ref<number>) {
  26. // ref coercing
  27. const coerced = ref(arg)
  28. expectType<Ref<number>>(coerced)
  29. // isRef as type guard
  30. if (isRef(arg)) {
  31. expectType<Ref<number>>(arg)
  32. }
  33. // ref unwrapping
  34. expectType<number>(unref(arg))
  35. expectType<number>(toValue(arg))
  36. expectType<number>(toValue(() => 123))
  37. // ref inner type should be unwrapped
  38. const nestedRef = ref({
  39. foo: ref(1),
  40. })
  41. expectType<{ foo: number }>(nestedRef.value)
  42. // ref boolean
  43. const falseRef = ref(false)
  44. expectType<Ref<boolean>>(falseRef)
  45. expectType<boolean>(falseRef.value)
  46. // ref true
  47. const trueRef = ref<true>(true)
  48. expectType<Ref<true>>(trueRef)
  49. expectType<true>(trueRef.value)
  50. // tuple
  51. expectType<[number, string]>(unref(ref([1, '1'])))
  52. interface IteratorFoo {
  53. [Symbol.iterator]: any
  54. }
  55. // with symbol
  56. expectType<Ref<IteratorFoo | null | undefined>>(
  57. ref<IteratorFoo | null | undefined>(),
  58. )
  59. // should not unwrap ref inside arrays
  60. const arr = ref([1, new Map<string, any>(), ref('1')]).value
  61. const value = arr[0]
  62. if (isRef(value)) {
  63. expectType<Ref>(value)
  64. } else if (typeof value === 'number') {
  65. expectType<number>(value)
  66. } else {
  67. // should narrow down to Map type
  68. // and not contain any Ref type
  69. expectType<Map<string, any>>(value)
  70. }
  71. // should still unwrap in objects nested in arrays
  72. const arr2 = ref([{ a: ref(1) }]).value
  73. expectType<number>(arr2[0].a)
  74. // any value should return Ref<any>, not any
  75. const a = ref(1 as any)
  76. expectType<IsAny<typeof a>>(false)
  77. }
  78. plainType(1)
  79. function bailType(arg: HTMLElement | Ref<HTMLElement>) {
  80. // ref coercing
  81. const coerced = ref(arg)
  82. expectType<Ref<HTMLElement>>(coerced)
  83. // isRef as type guard
  84. if (isRef(arg)) {
  85. expectType<Ref<HTMLElement>>(arg)
  86. }
  87. // ref unwrapping
  88. expectType<HTMLElement>(unref(arg))
  89. // ref inner type should be unwrapped
  90. const nestedRef = ref({ foo: ref(document.createElement('DIV')) })
  91. expectType<Ref<{ foo: HTMLElement }>>(nestedRef)
  92. expectType<{ foo: HTMLElement }>(nestedRef.value)
  93. }
  94. const el = document.createElement('DIV')
  95. bailType(el)
  96. function withSymbol() {
  97. const customSymbol = Symbol()
  98. const obj = {
  99. [Symbol.asyncIterator]: ref(1),
  100. [Symbol.hasInstance]: { a: ref('a') },
  101. [Symbol.isConcatSpreadable]: { b: ref(true) },
  102. [Symbol.iterator]: [ref(1)],
  103. [Symbol.match]: new Set<Ref<number>>(),
  104. [Symbol.matchAll]: new Map<number, Ref<string>>(),
  105. [Symbol.replace]: { arr: [ref('a')] },
  106. [Symbol.search]: { set: new Set<Ref<number>>() },
  107. [Symbol.species]: { map: new Map<number, Ref<string>>() },
  108. [Symbol.split]: new WeakSet<Ref<boolean>>(),
  109. [Symbol.toPrimitive]: new WeakMap<Ref<boolean>, string>(),
  110. [Symbol.toStringTag]: { weakSet: new WeakSet<Ref<boolean>>() },
  111. [Symbol.unscopables]: { weakMap: new WeakMap<Ref<boolean>, string>() },
  112. [customSymbol]: { arr: [ref(1)] },
  113. }
  114. const objRef = ref(obj)
  115. expectType<Ref<number>>(objRef.value[Symbol.asyncIterator])
  116. expectType<{ a: Ref<string> }>(objRef.value[Symbol.hasInstance])
  117. expectType<{ b: Ref<boolean> }>(objRef.value[Symbol.isConcatSpreadable])
  118. expectType<Ref<number>[]>(objRef.value[Symbol.iterator])
  119. expectType<Set<Ref<number>>>(objRef.value[Symbol.match])
  120. expectType<Map<number, Ref<string>>>(objRef.value[Symbol.matchAll])
  121. expectType<{ arr: Ref<string>[] }>(objRef.value[Symbol.replace])
  122. expectType<{ set: Set<Ref<number>> }>(objRef.value[Symbol.search])
  123. expectType<{ map: Map<number, Ref<string>> }>(objRef.value[Symbol.species])
  124. expectType<WeakSet<Ref<boolean>>>(objRef.value[Symbol.split])
  125. expectType<WeakMap<Ref<boolean>, string>>(objRef.value[Symbol.toPrimitive])
  126. expectType<{ weakSet: WeakSet<Ref<boolean>> }>(
  127. objRef.value[Symbol.toStringTag],
  128. )
  129. expectType<{ weakMap: WeakMap<Ref<boolean>, string> }>(
  130. objRef.value[Symbol.unscopables],
  131. )
  132. expectType<{ arr: Ref<number>[] }>(objRef.value[customSymbol])
  133. }
  134. withSymbol()
  135. const state = reactive({
  136. foo: {
  137. value: 1,
  138. label: 'bar',
  139. },
  140. })
  141. expectType<string>(state.foo.label)
  142. describe('ref with generic', <T extends { name: string }>() => {
  143. const r = {} as T
  144. const s = ref(r)
  145. expectType<string>(s.value.name)
  146. const rr = {} as MaybeRef<T>
  147. // should at least allow casting
  148. const ss = ref(rr) as Ref<T>
  149. expectType<string>(ss.value.name)
  150. })
  151. describe('allow getter and setter types to be unrelated', <T>() => {
  152. const a = { b: ref(0) }
  153. const c = ref(a)
  154. c.value = a
  155. const d = {} as T
  156. const e = ref(d)
  157. e.value = d
  158. const f = ref(ref(0))
  159. expectType<number>(f.value)
  160. // @ts-expect-error
  161. f.value = ref(1)
  162. })
  163. describe('correctly unwraps nested refs', () => {
  164. const obj = {
  165. n: 24,
  166. ref: ref(24),
  167. nestedRef: ref({ n: ref(0) }),
  168. }
  169. const a = ref(obj)
  170. expectType<number>(a.value.n)
  171. expectType<number>(a.value.ref)
  172. expectType<number>(a.value.nestedRef.n)
  173. const b = reactive({ a })
  174. expectType<number>(b.a.n)
  175. expectType<number>(b.a.ref)
  176. expectType<number>(b.a.nestedRef.n)
  177. })
  178. // computed
  179. describe('allow computed getter and setter types to be unrelated', () => {
  180. const obj = ref({
  181. name: 'foo',
  182. })
  183. const c = computed({
  184. get() {
  185. return JSON.stringify(obj.value)
  186. },
  187. set(val: typeof obj.value) {
  188. obj.value = val
  189. },
  190. })
  191. c.value = { name: 'bar' } // object
  192. expectType<string>(c.value)
  193. })
  194. describe('Type safety for `WritableComputedRef` and `ComputedRef`', () => {
  195. // @ts-expect-error
  196. const writableComputed: WritableComputedRef<string> = computed(() => '')
  197. // should allow
  198. const immutableComputed: ComputedRef<string> = writableComputed
  199. expectType<ComputedRef<string>>(immutableComputed)
  200. })
  201. // shallowRef
  202. type Status = 'initial' | 'ready' | 'invalidating'
  203. const shallowStatus = shallowRef<Status>('initial')
  204. if (shallowStatus.value === 'initial') {
  205. expectType<Ref<Status>>(shallowStatus)
  206. expectType<Status>(shallowStatus.value)
  207. shallowStatus.value = 'invalidating'
  208. }
  209. const refStatus = ref<Status>('initial')
  210. if (refStatus.value === 'initial') {
  211. expectType<Ref<Status>>(shallowStatus)
  212. expectType<Status>(shallowStatus.value)
  213. refStatus.value = 'invalidating'
  214. }
  215. {
  216. const shallow = shallowRef(1)
  217. expectType<Ref<number>>(shallow)
  218. expectType<ShallowRef<number>>(shallow)
  219. }
  220. {
  221. //#7852
  222. type Steps = { step: '1' } | { step: '2' }
  223. const shallowUnionGenParam = shallowRef<Steps>({ step: '1' })
  224. const shallowUnionAsCast = shallowRef({ step: '1' } as Steps)
  225. expectType<IsUnion<typeof shallowUnionGenParam>>(false)
  226. expectType<IsUnion<typeof shallowUnionAsCast>>(false)
  227. }
  228. {
  229. // any value should return Ref<any>, not any
  230. const a = shallowRef(1 as any)
  231. expectType<IsAny<typeof a>>(false)
  232. }
  233. describe('shallowRef with generic', <T extends { name: string }>() => {
  234. const r = {} as T
  235. const s = shallowRef(r)
  236. expectType<string>(s.value.name)
  237. expectType<ShallowRef<T>>(shallowRef(r))
  238. const rr = {} as MaybeRef<T>
  239. // should at least allow casting
  240. const ss = shallowRef(rr) as Ref<T> | ShallowRef<T>
  241. expectType<string>(ss.value.name)
  242. })
  243. {
  244. // should return ShallowRef<T> | Ref<T>, not ShallowRef<T | Ref<T>>
  245. expectType<ShallowRef<{ name: string }> | Ref<{ name: string }>>(
  246. shallowRef({} as MaybeRef<{ name: string }>),
  247. )
  248. expectType<ShallowRef<number> | Ref<string[]> | ShallowRef<string>>(
  249. shallowRef('' as Ref<string[]> | string | number),
  250. )
  251. }
  252. // proxyRefs: should return `reactive` directly
  253. const r1 = reactive({
  254. k: 'v',
  255. })
  256. const p1 = proxyRefs(r1)
  257. expectType<typeof r1>(p1)
  258. // proxyRefs: `ShallowUnwrapRef`
  259. const r2 = {
  260. a: ref(1),
  261. c: computed(() => 1),
  262. u: undefined,
  263. obj: {
  264. k: ref('foo'),
  265. },
  266. union: Math.random() > 0 - 5 ? ref({ name: 'yo' }) : null,
  267. }
  268. const p2 = proxyRefs(r2)
  269. expectType<number>(p2.a)
  270. expectType<number>(p2.c)
  271. expectType<undefined>(p2.u)
  272. expectType<Ref<string>>(p2.obj.k)
  273. expectType<{ name: string } | null>(p2.union)
  274. // toRef and toRefs
  275. {
  276. const obj: {
  277. a: number
  278. b: Ref<number>
  279. c: number | string
  280. } = {
  281. a: 1,
  282. b: ref(1),
  283. c: 1,
  284. }
  285. // toRef
  286. expectType<Ref<number>>(toRef(obj, 'a'))
  287. expectType<Ref<number>>(toRef(obj, 'b'))
  288. // Should not distribute Refs over union
  289. expectType<Ref<number | string>>(toRef(obj, 'c'))
  290. const array = reactive(['a', 'b'])
  291. expectType<Ref<string>>(toRef(array, '1'))
  292. expectType<Ref<string>>(toRef(array, '1', 'fallback'))
  293. const tuple: [string, number] = ['a', 1]
  294. expectType<Ref<string>>(toRef(tuple, '0'))
  295. expectType<Ref<number>>(toRef(tuple, '1'))
  296. expectType<Ref<number>>(toRef(() => 123))
  297. expectType<Ref<number | string>>(toRef(() => obj.c))
  298. const r = toRef(() => 123)
  299. // @ts-expect-error
  300. r.value = 234
  301. // toRefs
  302. expectType<{
  303. a: Ref<number>
  304. b: Ref<number>
  305. // Should not distribute Refs over union
  306. c: Ref<number | string>
  307. }>(toRefs(obj))
  308. // Both should not do any unwrapping
  309. const someReactive = shallowReactive({
  310. a: {
  311. b: ref(42),
  312. },
  313. })
  314. const toRefResult = toRef(someReactive, 'a')
  315. const toRefsResult = toRefs(someReactive)
  316. expectType<Ref<number>>(toRefResult.value.b)
  317. expectType<Ref<number>>(toRefsResult.a.value.b)
  318. // #5188
  319. const props = { foo: 1 } as { foo: any }
  320. const { foo } = toRefs(props)
  321. expectType<Ref<any>>(foo)
  322. }
  323. // toRef default value
  324. {
  325. const obj: { x?: number } = {}
  326. const x = toRef(obj, 'x', 1)
  327. expectType<Ref<number>>(x)
  328. }
  329. // readonly() + ref()
  330. expectType<Readonly<Ref<number>>>(readonly(ref(1)))
  331. // #2687
  332. interface AppData {
  333. state: 'state1' | 'state2' | 'state3'
  334. }
  335. const data: ToRefs<AppData> = toRefs(
  336. reactive({
  337. state: 'state1',
  338. }),
  339. )
  340. switch (data.state.value) {
  341. case 'state1':
  342. data.state.value = 'state2'
  343. break
  344. case 'state2':
  345. data.state.value = 'state3'
  346. break
  347. case 'state3':
  348. data.state.value = 'state1'
  349. break
  350. }
  351. // #3954
  352. function testUnrefGenerics<T>(p: T | Ref<T>) {
  353. expectType<T>(unref(p))
  354. }
  355. testUnrefGenerics(1)
  356. // #4771
  357. describe('shallow reactive in reactive', () => {
  358. const baz = reactive({
  359. foo: shallowReactive({
  360. a: {
  361. b: ref(42),
  362. },
  363. }),
  364. })
  365. const foo = toRef(baz, 'foo')
  366. expectType<Ref<number>>(foo.value.a.b)
  367. expectType<number>(foo.value.a.b.value)
  368. })
  369. describe('shallow ref in reactive', () => {
  370. const x = reactive({
  371. foo: shallowRef({
  372. bar: {
  373. baz: ref(123),
  374. qux: reactive({
  375. z: ref(123),
  376. }),
  377. },
  378. }),
  379. })
  380. expectType<Ref<number>>(x.foo.bar.baz)
  381. expectType<number>(x.foo.bar.qux.z)
  382. })
  383. describe('ref in shallow ref', () => {
  384. const x = shallowRef({
  385. a: ref(123),
  386. })
  387. expectType<Ref<number>>(x.value.a)
  388. })
  389. describe('reactive in shallow ref', () => {
  390. const x = shallowRef({
  391. a: reactive({
  392. b: ref(0),
  393. }),
  394. })
  395. expectType<number>(x.value.a.b)
  396. })
  397. describe('toRef <-> toValue', () => {
  398. function foo(
  399. a: MaybeRef<string>,
  400. b: () => string,
  401. c: MaybeRefOrGetter<string>,
  402. d: ComputedRef<string>,
  403. ) {
  404. const r = toRef(a)
  405. expectType<Ref<string>>(r)
  406. // writable
  407. r.value = 'foo'
  408. const rb = toRef(b)
  409. expectType<Readonly<Ref<string>>>(rb)
  410. // @ts-expect-error ref created from getter should be readonly
  411. rb.value = 'foo'
  412. const rc = toRef(c)
  413. expectType<Readonly<Ref<string> | Ref<string>>>(rc)
  414. // @ts-expect-error ref created from MaybeReadonlyRef should be readonly
  415. rc.value = 'foo'
  416. const rd = toRef(d)
  417. expectType<ComputedRef<string>>(rd)
  418. // @ts-expect-error ref created from computed ref should be readonly
  419. rd.value = 'foo'
  420. expectType<string>(toValue(a))
  421. expectType<string>(toValue(b))
  422. expectType<string>(toValue(c))
  423. expectType<string>(toValue(d))
  424. return {
  425. r: toValue(r),
  426. rb: toValue(rb),
  427. rc: toValue(rc),
  428. rd: toValue(rd),
  429. }
  430. }
  431. expectType<{
  432. r: string
  433. rb: string
  434. rc: string
  435. rd: string
  436. }>(
  437. foo(
  438. 'foo',
  439. () => 'bar',
  440. ref('baz'),
  441. computed(() => 'hi'),
  442. ),
  443. )
  444. })
  445. // unref
  446. // #8747
  447. declare const unref1: number | Ref<number> | ComputedRef<number>
  448. expectType<number>(unref(unref1))
  449. // #11356
  450. declare const unref2:
  451. | MaybeRef<string>
  452. | ShallowRef<string>
  453. | ComputedRef<string>
  454. | WritableComputedRef<string>
  455. expectType<string>(unref(unref2))
  456. // toValue
  457. expectType<number>(toValue(unref1))
  458. expectType<string>(toValue(unref2))
  459. // useTemplateRef
  460. const tRef = useTemplateRef('foo')
  461. expectType<TemplateRef>(tRef)
  462. const tRef2 = useTemplateRef<HTMLElement>('bar')
  463. expectType<TemplateRef<HTMLElement>>(tRef2)