ref.test-d.ts 14 KB

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