ref.test-d.ts 12 KB

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