fix(nuxt3): improve error types for useAsyncData and useFetch (#4210)

Co-authored-by: Daniel Roe <daniel@roe.dev>
This commit is contained in:
Alex Liu 2022-04-30 02:42:22 +08:00 committed by GitHub
parent 2a3fbb4c24
commit eb903bd66e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 29 additions and 16 deletions

View File

@ -6,7 +6,7 @@ This composable provides a convenient wrapper around [`useAsyncData`](/api/compo
```ts [Signature] ```ts [Signature]
function useFetch( function useFetch(
url: string, url: string | Request,
options?: UseFetchOptions options?: UseFetchOptions
): Promise<DataT> ): Promise<DataT>
@ -26,7 +26,7 @@ type DataT = {
data: Ref<DataT> data: Ref<DataT>
pending: Ref<boolean> pending: Ref<boolean>
refresh: () => Promise<void> refresh: () => Promise<void>
error: Ref<any> error: Ref<Error | boolean>
} }
``` ```

View File

@ -42,14 +42,14 @@ const getDefault = () => null
export function useAsyncData< export function useAsyncData<
DataT, DataT,
DataE = any, DataE = Error,
Transform extends _Transform<DataT> = _Transform<DataT, DataT>, Transform extends _Transform<DataT> = _Transform<DataT, DataT>,
PickKeys extends KeyOfRes<Transform> = KeyOfRes<Transform> PickKeys extends KeyOfRes<Transform> = KeyOfRes<Transform>
> ( > (
key: string, key: string,
handler: (ctx?: NuxtApp) => Promise<DataT>, handler: (ctx?: NuxtApp) => Promise<DataT>,
options: AsyncDataOptions<DataT, Transform, PickKeys> = {} options: AsyncDataOptions<DataT, Transform, PickKeys> = {}
): AsyncData<PickFrom<ReturnType<Transform>, PickKeys>, DataE> { ): AsyncData<PickFrom<ReturnType<Transform>, PickKeys>, DataE | null | true> {
// Validate arguments // Validate arguments
if (typeof key !== 'string') { if (typeof key !== 'string') {
throw new TypeError('asyncData key must be a string') throw new TypeError('asyncData key must be a string')
@ -174,14 +174,14 @@ export function useAsyncData<
export function useLazyAsyncData< export function useLazyAsyncData<
DataT, DataT,
DataE = any, DataE = Error,
Transform extends _Transform<DataT> = _Transform<DataT, DataT>, Transform extends _Transform<DataT> = _Transform<DataT, DataT>,
PickKeys extends KeyOfRes<Transform> = KeyOfRes<Transform> PickKeys extends KeyOfRes<Transform> = KeyOfRes<Transform>
> ( > (
key: string, key: string,
handler: (ctx?: NuxtApp) => Promise<DataT>, handler: (ctx?: NuxtApp) => Promise<DataT>,
options: Omit<AsyncDataOptions<DataT, Transform, PickKeys>, 'lazy'> = {} options: Omit<AsyncDataOptions<DataT, Transform, PickKeys>, 'lazy'> = {}
): AsyncData<PickFrom<ReturnType<Transform>, PickKeys>, DataE> { ): AsyncData<PickFrom<ReturnType<Transform>, PickKeys>, DataE | null | true> {
return useAsyncData(key, handler, { ...options, lazy: true }) return useAsyncData(key, handler, { ...options, lazy: true })
} }

View File

@ -11,15 +11,13 @@ export interface UseFetchOptions<
DataT, DataT,
Transform extends _Transform<DataT, any> = _Transform<DataT, DataT>, Transform extends _Transform<DataT, any> = _Transform<DataT, DataT>,
PickKeys extends KeyOfRes<Transform> = KeyOfRes<Transform> PickKeys extends KeyOfRes<Transform> = KeyOfRes<Transform>
> extends > extends AsyncDataOptions<DataT, Transform, PickKeys>, FetchOptions {
AsyncDataOptions<DataT, Transform, PickKeys>,
FetchOptions
{
key?: string key?: string
} }
export function useFetch< export function useFetch<
ResT = void, ResT = void,
ErrorT = Error,
ReqT extends FetchRequest = FetchRequest, ReqT extends FetchRequest = FetchRequest,
_ResT = ResT extends void ? FetchResult<ReqT> : ResT, _ResT = ResT extends void ? FetchResult<ReqT> : ResT,
Transform extends (res: _ResT) => any = (res: _ResT) => _ResT, Transform extends (res: _ResT) => any = (res: _ResT) => _ResT,
@ -53,8 +51,8 @@ export function useFetch<
] ]
} }
const asyncData = useAsyncData(key, () => { const asyncData = useAsyncData<_ResT, ErrorT, Transform, PickKeys>(key, () => {
return $fetch(_request.value, _fetchOptions) as Promise<_ResT> return $fetch(_request.value, _fetchOptions)
}, _asyncDataOptions) }, _asyncDataOptions)
return asyncData return asyncData
@ -62,6 +60,7 @@ export function useFetch<
export function useLazyFetch< export function useLazyFetch<
ResT = void, ResT = void,
ErrorT = Error,
ReqT extends string = string, ReqT extends string = string,
_ResT = ResT extends void ? FetchResult<ReqT> : ResT, _ResT = ResT extends void ? FetchResult<ReqT> : ResT,
Transform extends (res: _ResT) => any = (res: _ResT) => _ResT, Transform extends (res: _ResT) => any = (res: _ResT) => _ResT,
@ -70,5 +69,8 @@ export function useLazyFetch<
request: Ref<ReqT> | ReqT | (() => ReqT), request: Ref<ReqT> | ReqT | (() => ReqT),
opts: Omit<UseFetchOptions<_ResT, Transform, PickKeys>, 'lazy'> = {} opts: Omit<UseFetchOptions<_ResT, Transform, PickKeys>, 'lazy'> = {}
) { ) {
return useFetch(request, { ...opts, lazy: true }) return useFetch<ResT, ErrorT, ReqT, _ResT, Transform, PickKeys>(request, {
...opts,
lazy: true
})
} }

View File

@ -23,14 +23,18 @@ describe('API routes', () => {
expectTypeOf(useAsyncData('api-hey-with-pick', () => $fetch('/api/hey'), { pick: ['baz'] }).data).toMatchTypeOf<Ref<{ baz: string }>>() expectTypeOf(useAsyncData('api-hey-with-pick', () => $fetch('/api/hey'), { pick: ['baz'] }).data).toMatchTypeOf<Ref<{ baz: string }>>()
expectTypeOf(useAsyncData('api-other', () => $fetch('/api/other')).data).toMatchTypeOf<Ref<unknown>>() expectTypeOf(useAsyncData('api-other', () => $fetch('/api/other')).data).toMatchTypeOf<Ref<unknown>>()
expectTypeOf(useAsyncData<TestResponse>('api-generics', () => $fetch('/test')).data).toMatchTypeOf<Ref<TestResponse>>() expectTypeOf(useAsyncData<TestResponse>('api-generics', () => $fetch('/test')).data).toMatchTypeOf<Ref<TestResponse>>()
expectTypeOf(useAsyncData<any, string>('api-error-generics', () => $fetch('/test')).error).toMatchTypeOf<Ref<string>>()
expectTypeOf(useAsyncData('api-error-generics', () => $fetch('/error')).error).toMatchTypeOf<Ref<Error | true | null>>()
expectTypeOf(useAsyncData<any, string>('api-error-generics', () => $fetch('/error')).error).toMatchTypeOf<Ref<string | true | null>>()
expectTypeOf(useLazyAsyncData('lazy-api-hello', () => $fetch('/api/hello')).data).toMatchTypeOf<Ref<string>>() expectTypeOf(useLazyAsyncData('lazy-api-hello', () => $fetch('/api/hello')).data).toMatchTypeOf<Ref<string>>()
expectTypeOf(useLazyAsyncData('lazy-api-hey', () => $fetch('/api/hey')).data).toMatchTypeOf<Ref<{ foo:string, baz: string }>>() expectTypeOf(useLazyAsyncData('lazy-api-hey', () => $fetch('/api/hey')).data).toMatchTypeOf<Ref<{ foo:string, baz: string }>>()
expectTypeOf(useLazyAsyncData('lazy-api-hey-with-pick', () => $fetch('/api/hey'), { pick: ['baz'] }).data).toMatchTypeOf<Ref<{ baz: string }>>() expectTypeOf(useLazyAsyncData('lazy-api-hey-with-pick', () => $fetch('/api/hey'), { pick: ['baz'] }).data).toMatchTypeOf<Ref<{ baz: string }>>()
expectTypeOf(useLazyAsyncData('lazy-api-other', () => $fetch('/api/other')).data).toMatchTypeOf<Ref<unknown>>() expectTypeOf(useLazyAsyncData('lazy-api-other', () => $fetch('/api/other')).data).toMatchTypeOf<Ref<unknown>>()
expectTypeOf(useLazyAsyncData<TestResponse>('lazy-api-generics', () => $fetch('/test')).data).toMatchTypeOf<Ref<TestResponse>>() expectTypeOf(useLazyAsyncData<TestResponse>('lazy-api-generics', () => $fetch('/test')).data).toMatchTypeOf<Ref<TestResponse>>()
expectTypeOf(useLazyAsyncData<any, string>('lazy-error-generics', () => $fetch('/test')).error).toMatchTypeOf<Ref<string>>()
expectTypeOf(useLazyAsyncData('lazy-error-generics', () => $fetch('/error')).error).toMatchTypeOf<Ref<Error | true | null>>()
expectTypeOf(useLazyAsyncData<any, string>('lazy-error-generics', () => $fetch('/error')).error).toMatchTypeOf<Ref<string | true | null>>()
}) })
it('works with useFetch', () => { it('works with useFetch', () => {
@ -39,12 +43,19 @@ describe('API routes', () => {
expectTypeOf(useFetch('/api/hey', { pick: ['baz'] }).data).toMatchTypeOf<Ref<{ baz: string }>>() expectTypeOf(useFetch('/api/hey', { pick: ['baz'] }).data).toMatchTypeOf<Ref<{ baz: string }>>()
expectTypeOf(useFetch('/api/other').data).toMatchTypeOf<Ref<unknown>>() expectTypeOf(useFetch('/api/other').data).toMatchTypeOf<Ref<unknown>>()
expectTypeOf(useFetch<TestResponse>('/test').data).toMatchTypeOf<Ref<TestResponse>>() expectTypeOf(useFetch<TestResponse>('/test').data).toMatchTypeOf<Ref<TestResponse>>()
expectTypeOf(useFetch('/error').error).toMatchTypeOf<Ref<Error | null | true>>()
expectTypeOf(useFetch<any, string>('/error').error).toMatchTypeOf<Ref<string | null | true>>()
expectTypeOf(useLazyFetch('/api/hello').data).toMatchTypeOf<Ref<string>>() expectTypeOf(useLazyFetch('/api/hello').data).toMatchTypeOf<Ref<string>>()
expectTypeOf(useLazyFetch('/api/hey').data).toMatchTypeOf<Ref<{ foo:string, baz: string }>>() expectTypeOf(useLazyFetch('/api/hey').data).toMatchTypeOf<Ref<{ foo:string, baz: string }>>()
expectTypeOf(useLazyFetch('/api/hey', { pick: ['baz'] }).data).toMatchTypeOf<Ref<{ baz: string }>>() expectTypeOf(useLazyFetch('/api/hey', { pick: ['baz'] }).data).toMatchTypeOf<Ref<{ baz: string }>>()
expectTypeOf(useLazyFetch('/api/other').data).toMatchTypeOf<Ref<unknown>>() expectTypeOf(useLazyFetch('/api/other').data).toMatchTypeOf<Ref<unknown>>()
expectTypeOf(useLazyFetch('/api/other').data).toMatchTypeOf<Ref<unknown>>() expectTypeOf(useLazyFetch('/api/other').data).toMatchTypeOf<Ref<unknown>>()
expectTypeOf(useLazyFetch<TestResponse>('/test').data).toMatchTypeOf<Ref<TestResponse>>() expectTypeOf(useLazyFetch<TestResponse>('/test').data).toMatchTypeOf<Ref<TestResponse>>()
expectTypeOf(useLazyFetch('/error').error).toMatchTypeOf<Ref<Error | null | true>>()
expectTypeOf(useLazyFetch<any, string>('/error').error).toMatchTypeOf<Ref<string | null | true>>()
}) })
}) })