From 3a822c71772ceb22110e92af9379bacb8634900a Mon Sep 17 00:00:00 2001 From: David Tai Date: Mon, 25 Jul 2022 20:37:39 +0800 Subject: [PATCH] refactor(nuxt): enhance `useFetch` and `useLazyFetch` request type (#4825) Co-authored-by: Daniel Roe --- docs/content/2.guide/2.features/5.data-fetching.md | 2 +- docs/content/3.api/1.composables/use-fetch.md | 2 +- packages/nuxt/src/app/composables/fetch.ts | 8 ++++---- packages/nuxt/src/core/runtime/nitro/error.ts | 4 ++-- test/fixtures/basic/types.ts | 14 ++++++++++++++ 5 files changed, 22 insertions(+), 8 deletions(-) diff --git a/docs/content/2.guide/2.features/5.data-fetching.md b/docs/content/2.guide/2.features/5.data-fetching.md index 353eea17c2..09d0ef67ca 100644 --- a/docs/content/2.guide/2.features/5.data-fetching.md +++ b/docs/content/2.guide/2.features/5.data-fetching.md @@ -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. -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"} :: diff --git a/docs/content/3.api/1.composables/use-fetch.md b/docs/content/3.api/1.composables/use-fetch.md index 258ae0af7a..b7e45b128f 100644 --- a/docs/content/3.api/1.composables/use-fetch.md +++ b/docs/content/3.api/1.composables/use-fetch.md @@ -1,6 +1,6 @@ # `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 diff --git a/packages/nuxt/src/app/composables/fetch.ts b/packages/nuxt/src/app/composables/fetch.ts index 93ad83e5fd..38a9594ed4 100644 --- a/packages/nuxt/src/app/composables/fetch.ts +++ b/packages/nuxt/src/app/composables/fetch.ts @@ -1,4 +1,4 @@ -import type { FetchOptions, FetchRequest } from 'ohmyfetch' +import type { FetchOptions } from 'ohmyfetch' import type { TypedInternalResponse, NitroFetchRequest } from 'nitropack' import { computed, isRef, Ref } from 'vue' import type { AsyncDataOptions, _Transform, KeyOfRes, AsyncData, PickFrom } from './asyncData' @@ -48,11 +48,11 @@ export function useFetch< const key = '$f' + _key const _request = computed(() => { - let r = request as Ref | FetchRequest | (() => FetchRequest) + let r = request if (typeof r === 'function') { r = r() } - return (isRef(r) ? r.value : r) as NitroFetchRequest + return (isRef(r) ? r.value : r) }) const { @@ -85,7 +85,7 @@ export function useFetch< } const asyncData = useAsyncData<_ResT, ErrorT, Transform, PickKeys>(key, () => { - return $fetch(_request.value, _fetchOptions) + return $fetch(_request.value, _fetchOptions) as Promise<_ResT> }, _asyncDataOptions) return asyncData diff --git a/packages/nuxt/src/core/runtime/nitro/error.ts b/packages/nuxt/src/core/runtime/nitro/error.ts index a34db5c670..74ab0f78b9 100644 --- a/packages/nuxt/src/core/runtime/nitro/error.ts +++ b/packages/nuxt/src/core/runtime/nitro/error.ts @@ -1,5 +1,5 @@ import { withQuery } from 'ufo' -import type { NitroErrorHandler, NitroFetchRequest } from 'nitropack' +import type { NitroErrorHandler } from 'nitropack' // @ts-ignore TODO import { normalizeError, isJsonRequest } from '#internal/nitro/utils' @@ -36,7 +36,7 @@ export default async function errorhandler (_error, event) { } // 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) => { console.error('[nitro] Error while generating error response', error) return errorObject.statusMessage diff --git a/test/fixtures/basic/types.ts b/test/fixtures/basic/types.ts index 5b6afad4ac..0a12b4d9fe 100644 --- a/test/fixtures/basic/types.ts +++ b/test/fixtures/basic/types.ts @@ -135,6 +135,20 @@ describe('composables', () => { expectTypeOf(useFetch('/test', { default: () => 500 }).data).toMatchTypeOf>() }) + 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>>() + + // request param should infer string literal type / show auto-complete hint base on server routes, ex: '/api/hello' + expectTypeOf(useFetch('/api/hello').data).toMatchTypeOf>() + expectTypeOf(useLazyFetch('/api/hello').data).toMatchTypeOf>() + + // request can accept string literal and Request object type + expectTypeOf(useFetch('https://example.com/api').data).toMatchTypeOf>>() + expectTypeOf(useFetch(new Request('test')).data).toMatchTypeOf>>() + }) + it('provides proper type support when using overloads', () => { expectTypeOf(useState('test')).toMatchTypeOf(useState()) expectTypeOf(useState('test', () => ({ foo: Math.random() }))).toMatchTypeOf(useState(() => ({ foo: Math.random() })))