From dfdc4af71404a49450beb579dc34d38c3f12f8ea Mon Sep 17 00:00:00 2001 From: xjccc <546534045@qq.com> Date: Wed, 27 Nov 2024 16:51:07 +0800 Subject: [PATCH 1/7] fix: asyncData default types --- docs/2.guide/4.recipes/3.custom-usefetch.md | 22 ------------------- .../nuxt/src/app/composables/asyncData.ts | 14 ++++++------ packages/nuxt/src/app/composables/fetch.ts | 10 ++++----- playground/composables/test.ts | 14 ++++++++++++ playground/composables/useFetchCustom.ts | 11 ++++++++++ playground/plugins/fetchCustom.ts | 11 ++++++++++ 6 files changed, 48 insertions(+), 34 deletions(-) create mode 100644 playground/composables/test.ts create mode 100644 playground/composables/useFetchCustom.ts create mode 100644 playground/plugins/fetchCustom.ts diff --git a/docs/2.guide/4.recipes/3.custom-usefetch.md b/docs/2.guide/4.recipes/3.custom-usefetch.md index 45d9651237..9b09997915 100644 --- a/docs/2.guide/4.recipes/3.custom-usefetch.md +++ b/docs/2.guide/4.recipes/3.custom-usefetch.md @@ -90,28 +90,6 @@ const { data: modules } = await useAPI('/modules') ``` -If you want to customize the type of any error returned, you can also do so: - -```ts -import type { FetchError } from 'ofetch' -import type { UseFetchOptions } from 'nuxt/app' - -interface CustomError { - message: string - statusCode: number -} - -export function useAPI( - url: string | (() => string), - options?: UseFetchOptions, -) { - return useFetch>(url, { - ...options, - $fetch: useNuxtApp().$api - }) -} -``` - ::note This example demonstrates how to use a custom `useFetch`, but the same structure is identical for a custom `useAsyncData`. :: diff --git a/packages/nuxt/src/app/composables/asyncData.ts b/packages/nuxt/src/app/composables/asyncData.ts index 29193a0616..27399fc96b 100644 --- a/packages/nuxt/src/app/composables/asyncData.ts +++ b/packages/nuxt/src/app/composables/asyncData.ts @@ -42,7 +42,7 @@ export interface AsyncDataOptions< ResT, DataT = ResT, PickKeys extends KeysOf = KeysOf, - DefaultT = undefined, + DefaultT = DataT | undefined, > { /** * Whether to fetch on the server side. @@ -131,7 +131,7 @@ export function useAsyncData< NuxtErrorDataT = unknown, DataT = ResT, PickKeys extends KeysOf = KeysOf, - DefaultT = undefined, + DefaultT = DataT | undefined, > ( handler: (ctx?: NuxtApp) => Promise, options?: AsyncDataOptions @@ -164,7 +164,7 @@ export function useAsyncData< NuxtErrorDataT = unknown, DataT = ResT, PickKeys extends KeysOf = KeysOf, - DefaultT = undefined, + DefaultT = DataT | undefined, > ( key: string, handler: (ctx?: NuxtApp) => Promise, @@ -193,7 +193,7 @@ export function useAsyncData< NuxtErrorDataT = unknown, DataT = ResT, PickKeys extends KeysOf = KeysOf, - DefaultT = undefined, + DefaultT = DataT | undefined, > (...args: any[]): AsyncData, (NuxtErrorDataT extends Error | NuxtError ? NuxtErrorDataT : NuxtError) | undefined> { const autoKey = typeof args[args.length - 1] === 'string' ? args.pop() : undefined if (typeof args[0] !== 'string') { args.unshift(autoKey) } @@ -405,7 +405,7 @@ export function useLazyAsyncData< DataE = Error, DataT = ResT, PickKeys extends KeysOf = KeysOf, - DefaultT = undefined, + DefaultT = DataT | undefined, > ( handler: (ctx?: NuxtApp) => Promise, options?: Omit, 'lazy'> @@ -425,7 +425,7 @@ export function useLazyAsyncData< DataE = Error, DataT = ResT, PickKeys extends KeysOf = KeysOf, - DefaultT = undefined, + DefaultT = DataT | undefined, > ( key: string, handler: (ctx?: NuxtApp) => Promise, @@ -448,7 +448,7 @@ export function useLazyAsyncData< DataE = Error, DataT = ResT, PickKeys extends KeysOf = KeysOf, - DefaultT = undefined, + DefaultT = DataT | undefined, > (...args: any[]): AsyncData | DefaultT, DataE | undefined> { const autoKey = typeof args[args.length - 1] === 'string' ? args.pop() : undefined if (typeof args[0] !== 'string') { args.unshift(autoKey) } diff --git a/packages/nuxt/src/app/composables/fetch.ts b/packages/nuxt/src/app/composables/fetch.ts index 5ce5a87d1f..3ffa4709b2 100644 --- a/packages/nuxt/src/app/composables/fetch.ts +++ b/packages/nuxt/src/app/composables/fetch.ts @@ -31,7 +31,7 @@ export interface UseFetchOptions< ResT, DataT = ResT, PickKeys extends KeysOf = KeysOf, - DefaultT = undefined, + DefaultT = DataT | undefined, R extends NitroFetchRequest = string & {}, M extends AvailableRouterMethod = AvailableRouterMethod, > extends Omit, 'watch'>, ComputedFetchOptions { @@ -55,7 +55,7 @@ export function useFetch< _ResT = ResT extends void ? FetchResult : ResT, DataT = _ResT, PickKeys extends KeysOf = KeysOf, - DefaultT = undefined, + DefaultT = DataT | undefined, > ( request: Ref | ReqT | (() => ReqT), opts?: UseFetchOptions<_ResT, DataT, PickKeys, DefaultT, ReqT, Method> @@ -87,7 +87,7 @@ export function useFetch< _ResT = ResT extends void ? FetchResult : ResT, DataT = _ResT, PickKeys extends KeysOf = KeysOf, - DefaultT = undefined, + DefaultT = DataT | undefined, > ( request: Ref | ReqT | (() => ReqT), arg1?: string | UseFetchOptions<_ResT, DataT, PickKeys, DefaultT, ReqT, Method>, @@ -193,7 +193,7 @@ export function useLazyFetch< _ResT = ResT extends void ? FetchResult : ResT, DataT = _ResT, PickKeys extends KeysOf = KeysOf, - DefaultT = undefined, + DefaultT = DataT | undefined, > ( request: Ref | ReqT | (() => ReqT), opts?: Omit, 'lazy'> @@ -219,7 +219,7 @@ export function useLazyFetch< _ResT = ResT extends void ? FetchResult : ResT, DataT = _ResT, PickKeys extends KeysOf = KeysOf, - DefaultT = undefined, + DefaultT = DataT | undefined, > ( request: Ref | ReqT | (() => ReqT), arg1?: string | Omit, 'lazy'>, diff --git a/playground/composables/test.ts b/playground/composables/test.ts new file mode 100644 index 0000000000..cf920f9f3c --- /dev/null +++ b/playground/composables/test.ts @@ -0,0 +1,14 @@ +interface ResT { + foo: string[] + bar: string[] +} + +const { data } = await useFetchCustom('/some/endpoint', { + default: () => ({ + foo: [], + bar: [], + }), +}) +if (data.value) { + const a = data.value +} diff --git a/playground/composables/useFetchCustom.ts b/playground/composables/useFetchCustom.ts new file mode 100644 index 0000000000..7add2deca0 --- /dev/null +++ b/playground/composables/useFetchCustom.ts @@ -0,0 +1,11 @@ +import type { UseFetchOptions } from 'nuxt/app' + +export function useFetchCustom ( + url: string | (() => string), + options?: UseFetchOptions, +) { + return useFetch(url, { + ...options, + $fetch: useNuxtApp().$customFetch as typeof $fetch, + }) +} diff --git a/playground/plugins/fetchCustom.ts b/playground/plugins/fetchCustom.ts new file mode 100644 index 0000000000..2e208c1a62 --- /dev/null +++ b/playground/plugins/fetchCustom.ts @@ -0,0 +1,11 @@ +export default defineNuxtPlugin(() => { + const fetchCustom = $fetch.create({ + baseURL: 'https://this-doesnt-matter.com', + }) + + return { + provide: { + fetchCustom, + }, + } +}) From 791f7509742584adee30c6794b69ae429b479393 Mon Sep 17 00:00:00 2001 From: xjccc <546534045@qq.com> Date: Wed, 27 Nov 2024 18:09:58 +0800 Subject: [PATCH 2/7] feat: options add type DataT --- packages/nuxt/src/app/composables/asyncData.ts | 12 ++++++------ packages/nuxt/src/app/composables/fetch.ts | 8 ++++---- playground/composables/test.ts | 14 -------------- playground/composables/useFetchCustom.ts | 11 ----------- playground/plugins/fetchCustom.ts | 11 ----------- 5 files changed, 10 insertions(+), 46 deletions(-) delete mode 100644 playground/composables/test.ts delete mode 100644 playground/composables/useFetchCustom.ts delete mode 100644 playground/plugins/fetchCustom.ts diff --git a/packages/nuxt/src/app/composables/asyncData.ts b/packages/nuxt/src/app/composables/asyncData.ts index 27399fc96b..8ad7f0905f 100644 --- a/packages/nuxt/src/app/composables/asyncData.ts +++ b/packages/nuxt/src/app/composables/asyncData.ts @@ -131,7 +131,7 @@ export function useAsyncData< NuxtErrorDataT = unknown, DataT = ResT, PickKeys extends KeysOf = KeysOf, - DefaultT = DataT | undefined, + DefaultT = undefined, > ( handler: (ctx?: NuxtApp) => Promise, options?: AsyncDataOptions @@ -164,7 +164,7 @@ export function useAsyncData< NuxtErrorDataT = unknown, DataT = ResT, PickKeys extends KeysOf = KeysOf, - DefaultT = DataT | undefined, + DefaultT = undefined, > ( key: string, handler: (ctx?: NuxtApp) => Promise, @@ -193,7 +193,7 @@ export function useAsyncData< NuxtErrorDataT = unknown, DataT = ResT, PickKeys extends KeysOf = KeysOf, - DefaultT = DataT | undefined, + DefaultT = undefined, > (...args: any[]): AsyncData, (NuxtErrorDataT extends Error | NuxtError ? NuxtErrorDataT : NuxtError) | undefined> { const autoKey = typeof args[args.length - 1] === 'string' ? args.pop() : undefined if (typeof args[0] !== 'string') { args.unshift(autoKey) } @@ -405,7 +405,7 @@ export function useLazyAsyncData< DataE = Error, DataT = ResT, PickKeys extends KeysOf = KeysOf, - DefaultT = DataT | undefined, + DefaultT = undefined, > ( handler: (ctx?: NuxtApp) => Promise, options?: Omit, 'lazy'> @@ -425,7 +425,7 @@ export function useLazyAsyncData< DataE = Error, DataT = ResT, PickKeys extends KeysOf = KeysOf, - DefaultT = DataT | undefined, + DefaultT = undefined, > ( key: string, handler: (ctx?: NuxtApp) => Promise, @@ -448,7 +448,7 @@ export function useLazyAsyncData< DataE = Error, DataT = ResT, PickKeys extends KeysOf = KeysOf, - DefaultT = DataT | undefined, + DefaultT = undefined, > (...args: any[]): AsyncData | DefaultT, DataE | undefined> { const autoKey = typeof args[args.length - 1] === 'string' ? args.pop() : undefined if (typeof args[0] !== 'string') { args.unshift(autoKey) } diff --git a/packages/nuxt/src/app/composables/fetch.ts b/packages/nuxt/src/app/composables/fetch.ts index 3ffa4709b2..c5b9628ad7 100644 --- a/packages/nuxt/src/app/composables/fetch.ts +++ b/packages/nuxt/src/app/composables/fetch.ts @@ -55,7 +55,7 @@ export function useFetch< _ResT = ResT extends void ? FetchResult : ResT, DataT = _ResT, PickKeys extends KeysOf = KeysOf, - DefaultT = DataT | undefined, + DefaultT = undefined, > ( request: Ref | ReqT | (() => ReqT), opts?: UseFetchOptions<_ResT, DataT, PickKeys, DefaultT, ReqT, Method> @@ -87,7 +87,7 @@ export function useFetch< _ResT = ResT extends void ? FetchResult : ResT, DataT = _ResT, PickKeys extends KeysOf = KeysOf, - DefaultT = DataT | undefined, + DefaultT = undefined, > ( request: Ref | ReqT | (() => ReqT), arg1?: string | UseFetchOptions<_ResT, DataT, PickKeys, DefaultT, ReqT, Method>, @@ -193,7 +193,7 @@ export function useLazyFetch< _ResT = ResT extends void ? FetchResult : ResT, DataT = _ResT, PickKeys extends KeysOf = KeysOf, - DefaultT = DataT | undefined, + DefaultT = undefined, > ( request: Ref | ReqT | (() => ReqT), opts?: Omit, 'lazy'> @@ -219,7 +219,7 @@ export function useLazyFetch< _ResT = ResT extends void ? FetchResult : ResT, DataT = _ResT, PickKeys extends KeysOf = KeysOf, - DefaultT = DataT | undefined, + DefaultT = undefined, > ( request: Ref | ReqT | (() => ReqT), arg1?: string | Omit, 'lazy'>, diff --git a/playground/composables/test.ts b/playground/composables/test.ts deleted file mode 100644 index cf920f9f3c..0000000000 --- a/playground/composables/test.ts +++ /dev/null @@ -1,14 +0,0 @@ -interface ResT { - foo: string[] - bar: string[] -} - -const { data } = await useFetchCustom('/some/endpoint', { - default: () => ({ - foo: [], - bar: [], - }), -}) -if (data.value) { - const a = data.value -} diff --git a/playground/composables/useFetchCustom.ts b/playground/composables/useFetchCustom.ts deleted file mode 100644 index 7add2deca0..0000000000 --- a/playground/composables/useFetchCustom.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { UseFetchOptions } from 'nuxt/app' - -export function useFetchCustom ( - url: string | (() => string), - options?: UseFetchOptions, -) { - return useFetch(url, { - ...options, - $fetch: useNuxtApp().$customFetch as typeof $fetch, - }) -} diff --git a/playground/plugins/fetchCustom.ts b/playground/plugins/fetchCustom.ts deleted file mode 100644 index 2e208c1a62..0000000000 --- a/playground/plugins/fetchCustom.ts +++ /dev/null @@ -1,11 +0,0 @@ -export default defineNuxtPlugin(() => { - const fetchCustom = $fetch.create({ - baseURL: 'https://this-doesnt-matter.com', - }) - - return { - provide: { - fetchCustom, - }, - } -}) From 39e36b2981a4860eaaf0b19e464c65ef4c0e619a Mon Sep 17 00:00:00 2001 From: xjccc <546534045@qq.com> Date: Fri, 29 Nov 2024 15:02:00 +0800 Subject: [PATCH 3/7] fix: fetch options type & asyncData error type --- docs/2.guide/4.recipes/3.custom-usefetch.md | 30 +++++++++++++++++++ .../nuxt/src/app/composables/asyncData.ts | 23 +++++++------- packages/nuxt/src/app/composables/fetch.ts | 25 ++++++++-------- test/fixtures/basic-types/types.ts | 15 ++++++++-- 4 files changed, 67 insertions(+), 26 deletions(-) diff --git a/docs/2.guide/4.recipes/3.custom-usefetch.md b/docs/2.guide/4.recipes/3.custom-usefetch.md index 9b09997915..631b429578 100644 --- a/docs/2.guide/4.recipes/3.custom-usefetch.md +++ b/docs/2.guide/4.recipes/3.custom-usefetch.md @@ -90,6 +90,36 @@ const { data: modules } = await useAPI('/modules') ``` +Let's use the new composable and have a nice and clean component: + +```vue [app.vue] + +``` + +If you want to customize the type of any error returned, you can also do so: + +```ts twoslash +import type { FetchError } from 'ofetch' +import type { UseFetchOptions } from 'nuxt/app' + +interface CustomError { + message: string + statusCode: number +} + +export function useAPI( + url: string | (() => string), + options?: UseFetchOptions, +) { + return useFetch>(url, { + ...options, + $fetch: useNuxtApp().$api + }) +} +``` + ::note This example demonstrates how to use a custom `useFetch`, but the same structure is identical for a custom `useAsyncData`. :: diff --git a/packages/nuxt/src/app/composables/asyncData.ts b/packages/nuxt/src/app/composables/asyncData.ts index 8ad7f0905f..3f0cc48ced 100644 --- a/packages/nuxt/src/app/composables/asyncData.ts +++ b/packages/nuxt/src/app/composables/asyncData.ts @@ -3,7 +3,6 @@ import type { MultiWatchSources, Ref } from 'vue' import type { NuxtApp } from '../nuxt' import { useNuxtApp } from '../nuxt' import { toArray } from '../utils' -import type { NuxtError } from './error' import { createError } from './error' import { onNuxtReady } from './ready' @@ -42,7 +41,7 @@ export interface AsyncDataOptions< ResT, DataT = ResT, PickKeys extends KeysOf = KeysOf, - DefaultT = DataT | undefined, + DefaultT = DataT, > { /** * Whether to fetch on the server side. @@ -63,7 +62,7 @@ export interface AsyncDataOptions< * An `undefined` return value will trigger a fetch. * Default is `key => nuxt.isHydrating ? nuxt.payload.data[key] : nuxt.static.data[key]` which only caches data when payloadExtraction is enabled. */ - getCachedData?: (key: string, nuxtApp: NuxtApp) => NoInfer | undefined + getCachedData?: (key: string, nuxtApp: NuxtApp) => NoInfer | undefined /** * A function that can be used to alter handler function result after resolving. * Do not use it along with the `pick` option. @@ -135,7 +134,7 @@ export function useAsyncData< > ( handler: (ctx?: NuxtApp) => Promise, options?: AsyncDataOptions -): AsyncData | DefaultT, (NuxtErrorDataT extends Error | NuxtError ? NuxtErrorDataT : NuxtError) | undefined> +): AsyncData | DefaultT, NuxtErrorDataT | undefined> /** * Provides access to data that resolves asynchronously in an SSR-friendly composable. * See {@link https://nuxt.com/docs/api/composables/use-async-data} @@ -151,7 +150,7 @@ export function useAsyncData< > ( handler: (ctx?: NuxtApp) => Promise, options?: AsyncDataOptions -): AsyncData | DefaultT, (NuxtErrorDataT extends Error | NuxtError ? NuxtErrorDataT : NuxtError) | undefined> +): AsyncData | DefaultT, NuxtErrorDataT | undefined> /** * Provides access to data that resolves asynchronously in an SSR-friendly composable. * See {@link https://nuxt.com/docs/api/composables/use-async-data} @@ -169,7 +168,7 @@ export function useAsyncData< key: string, handler: (ctx?: NuxtApp) => Promise, options?: AsyncDataOptions -): AsyncData | DefaultT, (NuxtErrorDataT extends Error | NuxtError ? NuxtErrorDataT : NuxtError) | undefined> +): AsyncData | DefaultT, NuxtErrorDataT | undefined> /** * Provides access to data that resolves asynchronously in an SSR-friendly composable. * See {@link https://nuxt.com/docs/api/composables/use-async-data} @@ -187,14 +186,14 @@ export function useAsyncData< key: string, handler: (ctx?: NuxtApp) => Promise, options?: AsyncDataOptions -): AsyncData | DefaultT, (NuxtErrorDataT extends Error | NuxtError ? NuxtErrorDataT : NuxtError) | undefined> +): AsyncData | DefaultT, NuxtErrorDataT | undefined> export function useAsyncData< ResT, NuxtErrorDataT = unknown, DataT = ResT, PickKeys extends KeysOf = KeysOf, DefaultT = undefined, -> (...args: any[]): AsyncData, (NuxtErrorDataT extends Error | NuxtError ? NuxtErrorDataT : NuxtError) | undefined> { +> (...args: any[]): AsyncData, NuxtErrorDataT | undefined> { const autoKey = typeof args[args.length - 1] === 'string' ? args.pop() : undefined if (typeof args[0] !== 'string') { args.unshift(autoKey) } @@ -257,7 +256,7 @@ export function useAsyncData< } // TODO: Else, somehow check for conflicting keys with different defaults or fetcher - const asyncData = { ...nuxtApp._asyncData[key] } as { _default?: unknown } & AsyncData)> + const asyncData = { ...nuxtApp._asyncData[key] } as { _default?: unknown } & AsyncData // Don't expose default function to end user delete asyncData._default @@ -315,7 +314,7 @@ export function useAsyncData< // If this request is cancelled, resolve to the latest request. if ((promise as any).cancelled) { return nuxtApp._asyncDataPromises[key] } - asyncData.error.value = createError(error) as (NuxtErrorDataT extends Error | NuxtError ? NuxtErrorDataT : NuxtError) + asyncData.error.value = createError(error) as NuxtErrorDataT asyncData.data.value = unref(options.default!()) asyncData.status.value = 'error' }) @@ -394,10 +393,10 @@ export function useAsyncData< } // Allow directly awaiting on asyncData - const asyncDataPromise = Promise.resolve(nuxtApp._asyncDataPromises[key]).then(() => asyncData) as AsyncData)> + const asyncDataPromise = Promise.resolve(nuxtApp._asyncDataPromises[key]).then(() => asyncData) as AsyncData Object.assign(asyncDataPromise, asyncData) - return asyncDataPromise as AsyncData, (NuxtErrorDataT extends Error | NuxtError ? NuxtErrorDataT : NuxtError)> + return asyncDataPromise as AsyncData, NuxtErrorDataT | undefined> } /** @since 3.0.0 */ export function useLazyAsyncData< diff --git a/packages/nuxt/src/app/composables/fetch.ts b/packages/nuxt/src/app/composables/fetch.ts index c5b9628ad7..659e013dcc 100644 --- a/packages/nuxt/src/app/composables/fetch.ts +++ b/packages/nuxt/src/app/composables/fetch.ts @@ -29,12 +29,13 @@ type ComputedFetchOptions = ResT extends void ? 'get' extends AvailableRouterMethod ? 'get' : AvailableRouterMethod : AvailableRouterMethod, + _ResT = ResT extends void ? FetchResult : ResT, + DataT = _ResT, PickKeys extends KeysOf = KeysOf, - DefaultT = DataT | undefined, - R extends NitroFetchRequest = string & {}, - M extends AvailableRouterMethod = AvailableRouterMethod, -> extends Omit, 'watch'>, ComputedFetchOptions { + DefaultT = DataT, +> extends Omit, 'watch'>, ComputedFetchOptions { key?: string $fetch?: typeof globalThis.$fetch watch?: MultiWatchSources | false @@ -58,7 +59,7 @@ export function useFetch< DefaultT = undefined, > ( request: Ref | ReqT | (() => ReqT), - opts?: UseFetchOptions<_ResT, DataT, PickKeys, DefaultT, ReqT, Method> + opts?: UseFetchOptions<_ResT, ReqT, Method, _ResT, DataT, PickKeys, DefaultT> ): AsyncData | DefaultT, ErrorT | undefined> /** * Fetch data from an API endpoint with an SSR-friendly composable. @@ -77,7 +78,7 @@ export function useFetch< DefaultT = DataT, > ( request: Ref | ReqT | (() => ReqT), - opts?: UseFetchOptions<_ResT, DataT, PickKeys, DefaultT, ReqT, Method> + opts?: UseFetchOptions<_ResT, ReqT, Method, _ResT, DataT, PickKeys, DefaultT> ): AsyncData | DefaultT, ErrorT | undefined> export function useFetch< ResT = void, @@ -90,7 +91,7 @@ export function useFetch< DefaultT = undefined, > ( request: Ref | ReqT | (() => ReqT), - arg1?: string | UseFetchOptions<_ResT, DataT, PickKeys, DefaultT, ReqT, Method>, + arg1?: string | UseFetchOptions<_ResT, ReqT, Method, _ResT, DataT, PickKeys, DefaultT>, arg2?: string, ) { const [opts = {}, autoKey] = typeof arg1 === 'string' ? [{}, arg1] : [arg1, arg2] @@ -196,7 +197,7 @@ export function useLazyFetch< DefaultT = undefined, > ( request: Ref | ReqT | (() => ReqT), - opts?: Omit, 'lazy'> + opts?: Omit, 'lazy'> ): AsyncData | DefaultT, ErrorT | undefined> export function useLazyFetch< ResT = void, @@ -209,7 +210,7 @@ export function useLazyFetch< DefaultT = DataT, > ( request: Ref | ReqT | (() => ReqT), - opts?: Omit, 'lazy'> + opts?: Omit, 'lazy'> ): AsyncData | DefaultT, ErrorT | undefined> export function useLazyFetch< ResT = void, @@ -222,7 +223,7 @@ export function useLazyFetch< DefaultT = undefined, > ( request: Ref | ReqT | (() => ReqT), - arg1?: string | Omit, 'lazy'>, + arg1?: string | Omit, 'lazy'>, arg2?: string, ) { const [opts = {}, autoKey] = typeof arg1 === 'string' ? [{}, arg1] : [arg1, arg2] @@ -240,7 +241,7 @@ export function useLazyFetch< autoKey) } -function generateOptionSegments<_ResT, DataT, DefaultT> (opts: UseFetchOptions<_ResT, DataT, any, DefaultT, any, any>) { +function generateOptionSegments<_ResT, DataT, DefaultT> (opts: UseFetchOptions<_ResT, any, any, _ResT, DataT, any, DefaultT>) { const segments: Array> = [ toValue(opts.method as MaybeRef | undefined)?.toUpperCase() || 'GET', toValue(opts.baseURL), diff --git a/test/fixtures/basic-types/types.ts b/test/fixtures/basic-types/types.ts index 5b9f7b9883..f549c59175 100644 --- a/test/fixtures/basic-types/types.ts +++ b/test/fixtures/basic-types/types.ts @@ -43,8 +43,8 @@ describe('API routes', () => { expectTypeOf(useAsyncData('api-other', () => $fetch('/api/other')).data).toEqualTypeOf>() expectTypeOf(useAsyncData('api-generics', () => $fetch('/test')).data).toEqualTypeOf>() - expectTypeOf(useAsyncData('api-error-generics', () => $fetch('/error')).error).toEqualTypeOf | DefaultAsyncDataErrorValue>>() - expectTypeOf(useAsyncData('api-error-generics', () => $fetch('/error')).error).toEqualTypeOf | DefaultAsyncDataErrorValue>>() + expectTypeOf(useAsyncData('api-error-generics', () => $fetch('/error')).error).toEqualTypeOf>() + expectTypeOf(useAsyncData('api-error-generics', () => $fetch('/error')).error).toEqualTypeOf>() // backwards compatibility expectTypeOf(useAsyncData('api-error-generics', () => $fetch('/error')).error).toEqualTypeOf>() expectTypeOf(useAsyncData>('api-error-generics', () => $fetch('/error')).error).toEqualTypeOf | DefaultAsyncDataErrorValue>>() @@ -469,6 +469,17 @@ describe('composables', () => { expectTypeOf(useLazyAsyncData(() => $fetch('/test'), { default: () => 'test', transform: () => 'transformed' }).data).toEqualTypeOf>() }) + it('correct types when using custom error type', () => { + interface CustomError { + message: string + code: number + } + expectTypeOf(useFetch('/test').error).toEqualTypeOf>() + expectTypeOf(useLazyFetch('/test').error).toEqualTypeOf>() + expectTypeOf(useAsyncData('custom-error-type', () => $fetch('/error')).error).toEqualTypeOf>() + expectTypeOf(useLazyAsyncData('custom-error-type', () => $fetch('/error')).error).toEqualTypeOf>() + }) + it('supports asynchronous transform', () => { const { data } = useAsyncData('test', () => $fetch('/test') as Promise<{ foo: 'bar' }>, { async transform (data) { From b04bf764e32598d23830ce9452ebd76a4fbb2f3c Mon Sep 17 00:00:00 2001 From: xjccc <546534045@qq.com> Date: Fri, 29 Nov 2024 15:10:01 +0800 Subject: [PATCH 4/7] fix: asyncData NuxtErrorDataT deafult to Error --- packages/nuxt/src/app/composables/asyncData.ts | 10 +++++----- test/fixtures/basic-types/types.ts | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/nuxt/src/app/composables/asyncData.ts b/packages/nuxt/src/app/composables/asyncData.ts index 3f0cc48ced..a809ff2f09 100644 --- a/packages/nuxt/src/app/composables/asyncData.ts +++ b/packages/nuxt/src/app/composables/asyncData.ts @@ -127,7 +127,7 @@ export type AsyncData = _AsyncData & Promise<_AsyncDat */ export function useAsyncData< ResT, - NuxtErrorDataT = unknown, + NuxtErrorDataT = Error, DataT = ResT, PickKeys extends KeysOf = KeysOf, DefaultT = undefined, @@ -143,7 +143,7 @@ export function useAsyncData< */ export function useAsyncData< ResT, - NuxtErrorDataT = unknown, + NuxtErrorDataT = Error, DataT = ResT, PickKeys extends KeysOf = KeysOf, DefaultT = DataT, @@ -160,7 +160,7 @@ export function useAsyncData< */ export function useAsyncData< ResT, - NuxtErrorDataT = unknown, + NuxtErrorDataT = Error, DataT = ResT, PickKeys extends KeysOf = KeysOf, DefaultT = undefined, @@ -178,7 +178,7 @@ export function useAsyncData< */ export function useAsyncData< ResT, - NuxtErrorDataT = unknown, + NuxtErrorDataT = Error, DataT = ResT, PickKeys extends KeysOf = KeysOf, DefaultT = DataT, @@ -189,7 +189,7 @@ export function useAsyncData< ): AsyncData | DefaultT, NuxtErrorDataT | undefined> export function useAsyncData< ResT, - NuxtErrorDataT = unknown, + NuxtErrorDataT = Error, DataT = ResT, PickKeys extends KeysOf = KeysOf, DefaultT = undefined, diff --git a/test/fixtures/basic-types/types.ts b/test/fixtures/basic-types/types.ts index f549c59175..3887cf5b45 100644 --- a/test/fixtures/basic-types/types.ts +++ b/test/fixtures/basic-types/types.ts @@ -43,7 +43,7 @@ describe('API routes', () => { expectTypeOf(useAsyncData('api-other', () => $fetch('/api/other')).data).toEqualTypeOf>() expectTypeOf(useAsyncData('api-generics', () => $fetch('/test')).data).toEqualTypeOf>() - expectTypeOf(useAsyncData('api-error-generics', () => $fetch('/error')).error).toEqualTypeOf>() + expectTypeOf(useAsyncData('api-error-generics', () => $fetch('/error')).error).toEqualTypeOf>() expectTypeOf(useAsyncData('api-error-generics', () => $fetch('/error')).error).toEqualTypeOf>() // backwards compatibility expectTypeOf(useAsyncData('api-error-generics', () => $fetch('/error')).error).toEqualTypeOf>() From d4cab63e04730f309dfd500a3193127d4ae9e3e1 Mon Sep 17 00:00:00 2001 From: xjccc <546534045@qq.com> Date: Fri, 29 Nov 2024 16:19:55 +0800 Subject: [PATCH 5/7] update: docs --- docs/2.guide/4.recipes/3.custom-usefetch.md | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/docs/2.guide/4.recipes/3.custom-usefetch.md b/docs/2.guide/4.recipes/3.custom-usefetch.md index 631b429578..ab6405ecec 100644 --- a/docs/2.guide/4.recipes/3.custom-usefetch.md +++ b/docs/2.guide/4.recipes/3.custom-usefetch.md @@ -69,10 +69,11 @@ Wrapping with [`useAsyncData`](/docs/api/composables/use-async-data) **avoid dou Now that `$api` has the logic we want, let's create a `useAPI` composable to replace the usage of `useAsyncData` + `$api`: ```ts [composables/useAPI.ts] +import type { NitroFetchRequest } from 'nitropack' import type { UseFetchOptions } from 'nuxt/app' export function useAPI( - url: string | (() => string), + url: NitroFetchRequest, options?: UseFetchOptions, ) { return useFetch(url, { @@ -90,18 +91,10 @@ const { data: modules } = await useAPI('/modules') ``` -Let's use the new composable and have a nice and clean component: - -```vue [app.vue] - -``` - If you want to customize the type of any error returned, you can also do so: ```ts twoslash -import type { FetchError } from 'ofetch' +import type { NitroFetchRequest } from 'nitropack' import type { UseFetchOptions } from 'nuxt/app' interface CustomError { @@ -110,10 +103,10 @@ interface CustomError { } export function useAPI( - url: string | (() => string), + url: NitroFetchRequest, options?: UseFetchOptions, ) { - return useFetch>(url, { + return useFetch(url, { ...options, $fetch: useNuxtApp().$api }) From 2a239ef52d990dd8766f2fb0c698aa3dd6c211a2 Mon Sep 17 00:00:00 2001 From: xjccc <546534045@qq.com> Date: Fri, 29 Nov 2024 17:22:16 +0800 Subject: [PATCH 6/7] update: transform type --- packages/nuxt/src/app/composables/asyncData.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/nuxt/src/app/composables/asyncData.ts b/packages/nuxt/src/app/composables/asyncData.ts index a809ff2f09..59fc441364 100644 --- a/packages/nuxt/src/app/composables/asyncData.ts +++ b/packages/nuxt/src/app/composables/asyncData.ts @@ -62,12 +62,12 @@ export interface AsyncDataOptions< * An `undefined` return value will trigger a fetch. * Default is `key => nuxt.isHydrating ? nuxt.payload.data[key] : nuxt.static.data[key]` which only caches data when payloadExtraction is enabled. */ - getCachedData?: (key: string, nuxtApp: NuxtApp) => NoInfer | undefined + getCachedData?: (key: string, nuxtApp: NuxtApp) => NoInfer | undefined /** * A function that can be used to alter handler function result after resolving. * Do not use it along with the `pick` option. */ - transform?: _Transform + transform?: _Transform /** * Only pick specified keys in this array from the handler function result. * Do not use it along with the `transform` option. @@ -293,7 +293,7 @@ export function useAsyncData< let result = _result as unknown as DataT if (options.transform) { - result = await options.transform(_result) + result = await options.transform(_result as any) } if (options.pick) { result = pick(result as any, options.pick) as DataT From d405c9a19098e008554ada32bd6945f43a53e0f1 Mon Sep 17 00:00:00 2001 From: xjccc <546534045@qq.com> Date: Mon, 2 Dec 2024 11:35:33 +0800 Subject: [PATCH 7/7] test: add custom fetch test --- .../basic-types/composables/useFetchCustom.ts | 25 +++++++++++++++ .../basic-types/plugins/fetchCustom.ts | 11 +++++++ test/fixtures/basic-types/types.ts | 32 +++++++++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 test/fixtures/basic-types/composables/useFetchCustom.ts create mode 100644 test/fixtures/basic-types/plugins/fetchCustom.ts diff --git a/test/fixtures/basic-types/composables/useFetchCustom.ts b/test/fixtures/basic-types/composables/useFetchCustom.ts new file mode 100644 index 0000000000..b02961a58e --- /dev/null +++ b/test/fixtures/basic-types/composables/useFetchCustom.ts @@ -0,0 +1,25 @@ +import type { AsyncDataOptions, UseFetchOptions } from 'nuxt/app' +import type { NitroFetchRequest } from 'nitro/types' + +interface CustomError { + code: string + message: string +} +export function useFetchCustom ( + url: NitroFetchRequest, + options?: UseFetchOptions, +) { + return useFetch(url, { + ...options, + $fetch: useNuxtApp().$customFetch as typeof $fetch, + }) +} + +export function useAsyncFetchCustom ( + url: NitroFetchRequest, + options?: AsyncDataOptions, +) { + return useAsyncData(() => $fetch(url as string), { + ...options, + }) +} diff --git a/test/fixtures/basic-types/plugins/fetchCustom.ts b/test/fixtures/basic-types/plugins/fetchCustom.ts new file mode 100644 index 0000000000..340e0d37f4 --- /dev/null +++ b/test/fixtures/basic-types/plugins/fetchCustom.ts @@ -0,0 +1,11 @@ +export default defineNuxtPlugin(() => { + const fetchCustom = $fetch.create({ + baseURL: '', + }) + + return { + provide: { + fetchCustom, + }, + } +}) diff --git a/test/fixtures/basic-types/types.ts b/test/fixtures/basic-types/types.ts index 3887cf5b45..2141352c66 100644 --- a/test/fixtures/basic-types/types.ts +++ b/test/fixtures/basic-types/types.ts @@ -6,6 +6,7 @@ import type { NavigationFailure, RouteLocationNormalized, RouteLocationRaw, Rout import type { AppConfig, RuntimeValue, UpperSnakeCase } from 'nuxt/schema' import { defineNuxtModule } from 'nuxt/kit' import { defineNuxtConfig } from 'nuxt/config' +import { useAsyncFetchCustom, useFetchCustom } from './composables/useFetchCustom' import { callWithNuxt, isVue3 } from '#app' import type { NuxtError } from '#app' import type { NavigateToOptions } from '#app/composables/router' @@ -470,14 +471,45 @@ describe('composables', () => { }) it('correct types when using custom error type', () => { + interface ResT { + foo: string + baz: string + } interface CustomError { + message: string + code: string + } + + interface OtherCustomError { message: string code: number } + expectTypeOf(useFetch('/test').error).toEqualTypeOf>() expectTypeOf(useLazyFetch('/test').error).toEqualTypeOf>() expectTypeOf(useAsyncData('custom-error-type', () => $fetch('/error')).error).toEqualTypeOf>() expectTypeOf(useLazyAsyncData('custom-error-type', () => $fetch('/error')).error).toEqualTypeOf>() + expectTypeOf(useLazyAsyncData('custom-error-type', () => $fetch('/error')).error).toEqualTypeOf>() + + expectTypeOf(useFetchCustom('/api/hey').data).toEqualTypeOf>() + expectTypeOf(useFetchCustom('/api/hey', { default: () => ({ foo: 'bar', baz: 'baz' }) }).data).toEqualTypeOf>() + expectTypeOf(useFetchCustom('/api/hey', { transform: () => ({ foo: 'bar', baz: 'baz' }) }).data).toEqualTypeOf>() + expectTypeOf(useFetchCustom('/api/hello').data).toEqualTypeOf>() + expectTypeOf(useFetchCustom('/api/hello', { default: () => 'default' }).data).toEqualTypeOf>() + expectTypeOf(useFetchCustom('/api/hello', { default: () => 'default', transform: () => 'transform' }).data).toEqualTypeOf>() + expectTypeOf(useFetchCustom('/api/hello', { transform: () => 'transform' }).data).toEqualTypeOf>() + expectTypeOf(useFetchCustom('/api/hello').error).toEqualTypeOf>() + expectTypeOf(useFetchCustom('/api/hello').error).toEqualTypeOf>() + + expectTypeOf(useAsyncFetchCustom('/api/hey').data).toEqualTypeOf>() + expectTypeOf(useAsyncFetchCustom('/api/hey', { default: () => ({ foo: 'bar', baz: 'baz' }) }).data).toEqualTypeOf>() + expectTypeOf(useAsyncFetchCustom('/api/hey', { transform: () => ({ foo: 'bar', baz: 'baz' }) }).data).toEqualTypeOf>() + expectTypeOf(useAsyncFetchCustom('/api/hello').data).toEqualTypeOf>() + expectTypeOf(useAsyncFetchCustom('/api/hello', { default: () => 'default' }).data).toEqualTypeOf>() + expectTypeOf(useAsyncFetchCustom('/api/hello', { default: () => 'default', transform: () => 'transform' }).data).toEqualTypeOf>() + expectTypeOf(useAsyncFetchCustom('/api/hello', { transform: () => 'transform' }).data).toEqualTypeOf>() + expectTypeOf(useAsyncFetchCustom('/api/hello').error).toEqualTypeOf>() + expectTypeOf(useAsyncFetchCustom('/api/hello').error).toEqualTypeOf>() }) it('supports asynchronous transform', () => {