feat(nuxt3): let useAsyncData() return value that error can defined type by generics (#4109)

This commit is contained in:
Alex Liu 2022-04-07 00:02:45 +08:00 committed by GitHub
parent 8284e7fa21
commit b7dc0931c4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 27 additions and 9 deletions

View File

@ -8,7 +8,7 @@ const {
data: Ref<DataT>, data: Ref<DataT>,
pending: Ref<boolean>, pending: Ref<boolean>,
refresh: () => Promise<void>, refresh: () => Promise<void>,
error?: any error: Ref<any>
} = useAsyncData( } = useAsyncData(
key: string, key: string,
handler: (ctx?: NuxtApp) => Promise<Object>, handler: (ctx?: NuxtApp) => Promise<Object>,

View File

@ -28,26 +28,27 @@ export interface RefreshOptions {
_initial?: boolean _initial?: boolean
} }
export interface _AsyncData<DataT> { export interface _AsyncData<DataT, ErrorT> {
data: Ref<DataT> data: Ref<DataT>
pending: Ref<boolean> pending: Ref<boolean>
refresh: (opts?: RefreshOptions) => Promise<void> refresh: (opts?: RefreshOptions) => Promise<void>
error?: any error: Ref<ErrorT>
} }
export type AsyncData<Data> = _AsyncData<Data> & Promise<_AsyncData<Data>> export type AsyncData<Data, Error> = _AsyncData<Data, Error> & Promise<_AsyncData<Data, Error>>
const getDefault = () => null const getDefault = () => null
export function useAsyncData< export function useAsyncData<
DataT, DataT,
DataE = any,
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>> { ): AsyncData<PickFrom<ReturnType<Transform>, PickKeys>, DataE> {
// 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')
@ -87,7 +88,7 @@ export function useAsyncData<
data: ref(nuxt.payload.data[key] ?? options.default()), data: ref(nuxt.payload.data[key] ?? options.default()),
pending: ref(!useInitialCache()), pending: ref(!useInitialCache()),
error: ref(nuxt.payload._errors[key] ?? null) error: ref(nuxt.payload._errors[key] ?? null)
} as AsyncData<DataT> } as AsyncData<DataT, DataE>
asyncData.refresh = (opts = {}) => { asyncData.refresh = (opts = {}) => {
// Avoid fetching same key more than once at a time // Avoid fetching same key more than once at a time
@ -164,21 +165,22 @@ export function useAsyncData<
} }
// Allow directly awaiting on asyncData // Allow directly awaiting on asyncData
const asyncDataPromise = Promise.resolve(nuxt._asyncDataPromises[key]).then(() => asyncData) as AsyncData<DataT> const asyncDataPromise = Promise.resolve(nuxt._asyncDataPromises[key]).then(() => asyncData) as AsyncData<DataT, DataE>
Object.assign(asyncDataPromise, asyncData) Object.assign(asyncDataPromise, asyncData)
return asyncDataPromise as AsyncData<PickFrom<ReturnType<Transform>, PickKeys>> return asyncDataPromise as AsyncData<PickFrom<ReturnType<Transform>, PickKeys>, DataE>
} }
export function useLazyAsyncData< export function useLazyAsyncData<
DataT, DataT,
DataE = any,
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>> { ): AsyncData<PickFrom<ReturnType<Transform>, PickKeys>, DataE> {
return useAsyncData(key, handler, { ...options, lazy: true }) return useAsyncData(key, handler, { ...options, lazy: true })
} }

View File

@ -17,6 +17,22 @@ describe('API routes', () => {
expectTypeOf($fetch<TestResponse>('/test')).toMatchTypeOf<Promise<TestResponse>>() expectTypeOf($fetch<TestResponse>('/test')).toMatchTypeOf<Promise<TestResponse>>()
}) })
it('works with useAsyncData', () => {
expectTypeOf(useAsyncData('api-hello', () => $fetch('/api/hello')).data).toMatchTypeOf<Ref<string>>()
expectTypeOf(useAsyncData('api-hey', () => $fetch('/api/hey')).data).toMatchTypeOf<Ref<{ foo:string, 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<TestResponse>('api-generics', () => $fetch('/test')).data).toMatchTypeOf<Ref<TestResponse>>()
expectTypeOf(useAsyncData<any, string>('api-error-generics', () => $fetch('/test')).error).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-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<TestResponse>('lazy-api-generics', () => $fetch('/test')).data).toMatchTypeOf<Ref<TestResponse>>()
expectTypeOf(useLazyAsyncData<any, string>('lazy-error-generics', () => $fetch('/test')).error).toMatchTypeOf<Ref<string>>()
})
it('works with useFetch', () => { it('works with useFetch', () => {
expectTypeOf(useFetch('/api/hello').data).toMatchTypeOf<Ref<string>>() expectTypeOf(useFetch('/api/hello').data).toMatchTypeOf<Ref<string>>()
expectTypeOf(useFetch('/api/hey').data).toMatchTypeOf<Ref<{ foo:string, baz: string }>>() expectTypeOf(useFetch('/api/hey').data).toMatchTypeOf<Ref<{ foo:string, baz: string }>>()