refactor(nuxt): enhance useFetch and useLazyFetch request type (#4825)

Co-authored-by: Daniel Roe <daniel@roe.dev>
This commit is contained in:
David Tai 2022-07-25 20:37:39 +08:00 committed by GitHub
parent 8298cf27e6
commit 3a822c7177
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 22 additions and 8 deletions

View File

@ -10,7 +10,7 @@ Nuxt provides `useFetch`, `useLazyFetch`, `useAsyncData` and `useLazyAsyncData`
Within your pages, components and plugins you can use `useFetch` to universally fetch from any URL. Within your pages, components and plugins you can use `useFetch` to universally fetch from any URL.
This composable provides a convenient wrapper around `useAsyncData` and `$fetch`. It automatically generates a key based on URL and fetch options, as well as infers API response type. This composable provides a convenient wrapper around `useAsyncData` and `$fetch`. It automatically generates a key based on URL and fetch options, provides type hints for request url based on server routes, and infers API response type.
::ReadMore{link="/api/composables/use-fetch"} ::ReadMore{link="/api/composables/use-fetch"}
:: ::

View File

@ -1,6 +1,6 @@
# `useFetch` # `useFetch`
This composable provides a convenient wrapper around [`useAsyncData`](/api/composables/use-async-data) and [`$fetch`](/api/utils/$fetch). It automatically generates a key based on URL and fetch options, as well as infers API response type. This composable provides a convenient wrapper around [`useAsyncData`](/api/composables/use-async-data) and [`$fetch`](/api/utils/$fetch). It automatically generates a key based on URL and fetch options, provides type hints for request url based on server routes, and infers API response type.
## Type ## Type

View File

@ -1,4 +1,4 @@
import type { FetchOptions, FetchRequest } from 'ohmyfetch' import type { FetchOptions } from 'ohmyfetch'
import type { TypedInternalResponse, NitroFetchRequest } from 'nitropack' import type { TypedInternalResponse, NitroFetchRequest } from 'nitropack'
import { computed, isRef, Ref } from 'vue' import { computed, isRef, Ref } from 'vue'
import type { AsyncDataOptions, _Transform, KeyOfRes, AsyncData, PickFrom } from './asyncData' import type { AsyncDataOptions, _Transform, KeyOfRes, AsyncData, PickFrom } from './asyncData'
@ -48,11 +48,11 @@ export function useFetch<
const key = '$f' + _key const key = '$f' + _key
const _request = computed(() => { const _request = computed(() => {
let r = request as Ref<FetchRequest> | FetchRequest | (() => FetchRequest) let r = request
if (typeof r === 'function') { if (typeof r === 'function') {
r = r() r = r()
} }
return (isRef(r) ? r.value : r) as NitroFetchRequest return (isRef(r) ? r.value : r)
}) })
const { const {
@ -85,7 +85,7 @@ export function useFetch<
} }
const asyncData = useAsyncData<_ResT, ErrorT, Transform, PickKeys>(key, () => { const asyncData = useAsyncData<_ResT, ErrorT, Transform, PickKeys>(key, () => {
return $fetch(_request.value, _fetchOptions) return $fetch(_request.value, _fetchOptions) as Promise<_ResT>
}, _asyncDataOptions) }, _asyncDataOptions)
return asyncData return asyncData

View File

@ -1,5 +1,5 @@
import { withQuery } from 'ufo' import { withQuery } from 'ufo'
import type { NitroErrorHandler, NitroFetchRequest } from 'nitropack' import type { NitroErrorHandler } from 'nitropack'
// @ts-ignore TODO // @ts-ignore TODO
import { normalizeError, isJsonRequest } from '#internal/nitro/utils' import { normalizeError, isJsonRequest } from '#internal/nitro/utils'
@ -36,7 +36,7 @@ export default <NitroErrorHandler> async function errorhandler (_error, event) {
} }
// HTML response // HTML response
const url = withQuery('/__nuxt_error', errorObject as any) as NitroFetchRequest const url = withQuery('/__nuxt_error', errorObject as any)
const html = await $fetch(url).catch((error) => { const html = await $fetch(url).catch((error) => {
console.error('[nitro] Error while generating error response', error) console.error('[nitro] Error while generating error response', error)
return errorObject.statusMessage return errorObject.statusMessage

View File

@ -135,6 +135,20 @@ describe('composables', () => {
expectTypeOf(useFetch('/test', { default: () => 500 }).data).toMatchTypeOf<Ref<number>>() expectTypeOf(useFetch('/test', { default: () => 500 }).data).toMatchTypeOf<Ref<number>>()
}) })
it('infer request url string literal from server/api routes', () => {
// request can accept dynamic string type
const dynamicStringUrl:string = 'https://example.com/api'
expectTypeOf(useFetch(dynamicStringUrl).data).toMatchTypeOf<Ref<Pick<unknown, never>>>()
// request param should infer string literal type / show auto-complete hint base on server routes, ex: '/api/hello'
expectTypeOf(useFetch('/api/hello').data).toMatchTypeOf<Ref<string>>()
expectTypeOf(useLazyFetch('/api/hello').data).toMatchTypeOf<Ref<string>>()
// request can accept string literal and Request object type
expectTypeOf(useFetch('https://example.com/api').data).toMatchTypeOf<Ref<Pick<unknown, never>>>()
expectTypeOf(useFetch(new Request('test')).data).toMatchTypeOf<Ref<Pick<unknown, never>>>()
})
it('provides proper type support when using overloads', () => { it('provides proper type support when using overloads', () => {
expectTypeOf(useState('test')).toMatchTypeOf(useState()) expectTypeOf(useState('test')).toMatchTypeOf(useState())
expectTypeOf(useState('test', () => ({ foo: Math.random() }))).toMatchTypeOf(useState(() => ({ foo: Math.random() }))) expectTypeOf(useState('test', () => ({ foo: Math.random() }))).toMatchTypeOf(useState(() => ({ foo: Math.random() })))