feat(nuxt): infer useFetch return based on the method (#18526)

This commit is contained in:
Dany Sluijk 2023-02-07 15:01:31 +01:00 committed by GitHub
parent 526a78095a
commit bae73c3650
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 25 additions and 13 deletions

View File

@ -1,25 +1,26 @@
import type { FetchError } from 'ofetch'
import type { TypedInternalResponse, NitroFetchOptions, NitroFetchRequest } from 'nitropack'
import type { TypedInternalResponse, NitroFetchOptions, NitroFetchRequest, AvailableRouterMethod } from 'nitropack'
import type { Ref } from 'vue'
import { computed, unref, reactive } from 'vue'
import { hash } from 'ohash'
import type { AsyncDataOptions, _Transform, KeyOfRes, AsyncData, PickFrom } from './asyncData'
import { useAsyncData } from './asyncData'
export type FetchResult<ReqT extends NitroFetchRequest> = TypedInternalResponse<ReqT, unknown>
export type FetchResult<ReqT extends NitroFetchRequest, M extends AvailableRouterMethod<ReqT>> = TypedInternalResponse<ReqT, unknown, M>
type ComputedOptions<T extends Record<string, any>> = {
[K in keyof T]: T[K] extends Function ? T[K] : T[K] extends Record<string, any> ? ComputedOptions<T[K]> | Ref<T[K]> | T[K] : Ref<T[K]> | T[K]
}
type ComputedFetchOptions<R extends NitroFetchRequest> = ComputedOptions<NitroFetchOptions<R>>
type ComputedFetchOptions<R extends NitroFetchRequest, M extends AvailableRouterMethod<R>> = ComputedOptions<NitroFetchOptions<R, M>>
export interface UseFetchOptions<
DataT,
Transform extends _Transform<DataT, any> = _Transform<DataT, DataT>,
PickKeys extends KeyOfRes<Transform> = KeyOfRes<Transform>,
R extends NitroFetchRequest = string & {}
> extends AsyncDataOptions<DataT, Transform, PickKeys>, ComputedFetchOptions<R> {
R extends NitroFetchRequest = string & {},
M extends AvailableRouterMethod<R> = AvailableRouterMethod<R>
> extends AsyncDataOptions<DataT, Transform, PickKeys>, ComputedFetchOptions<R, M> {
key?: string
}
@ -27,23 +28,25 @@ export function useFetch<
ResT = void,
ErrorT = FetchError,
ReqT extends NitroFetchRequest = NitroFetchRequest,
_ResT = ResT extends void ? FetchResult<ReqT> : ResT,
Method extends AvailableRouterMethod<ReqT> = 'get' extends AvailableRouterMethod<ReqT> ? 'get' : AvailableRouterMethod<ReqT>,
_ResT = ResT extends void ? FetchResult<ReqT, Method> : ResT,
Transform extends (res: _ResT) => any = (res: _ResT) => _ResT,
PickKeys extends KeyOfRes<Transform> = KeyOfRes<Transform>
> (
request: Ref<ReqT> | ReqT | (() => ReqT),
opts?: UseFetchOptions<_ResT, Transform, PickKeys, ReqT>
opts?: UseFetchOptions<_ResT, Transform, PickKeys, ReqT, Method>
): AsyncData<PickFrom<ReturnType<Transform>, PickKeys>, ErrorT | null>
export function useFetch<
ResT = void,
ErrorT = FetchError,
ReqT extends NitroFetchRequest = NitroFetchRequest,
_ResT = ResT extends void ? FetchResult<ReqT> : ResT,
Method extends AvailableRouterMethod<ReqT> = 'get' extends AvailableRouterMethod<ReqT> ? 'get' : AvailableRouterMethod<ReqT>,
_ResT = ResT extends void ? FetchResult<ReqT, Method> : ResT,
Transform extends (res: _ResT) => any = (res: _ResT) => _ResT,
PickKeys extends KeyOfRes<Transform> = KeyOfRes<Transform>
> (
request: Ref<ReqT> | ReqT | (() => ReqT),
arg1?: string | UseFetchOptions<_ResT, Transform, PickKeys, ReqT>,
arg1?: string | UseFetchOptions<_ResT, Transform, PickKeys, ReqT, Method>,
arg2?: string
) {
const [opts = {}, autoKey] = typeof arg1 === 'string' ? [{}, arg1] : [arg1, arg2]
@ -109,23 +112,25 @@ export function useLazyFetch<
ResT = void,
ErrorT = FetchError,
ReqT extends NitroFetchRequest = NitroFetchRequest,
_ResT = ResT extends void ? FetchResult<ReqT> : ResT,
Method extends AvailableRouterMethod<ReqT> = 'get' extends AvailableRouterMethod<ReqT> ? 'get' : AvailableRouterMethod<ReqT>,
_ResT = ResT extends void ? FetchResult<ReqT, Method> : ResT,
Transform extends (res: _ResT) => any = (res: _ResT) => _ResT,
PickKeys extends KeyOfRes<Transform> = KeyOfRes<Transform>
> (
request: Ref<ReqT> | ReqT | (() => ReqT),
opts?: Omit<UseFetchOptions<_ResT, Transform, PickKeys>, 'lazy'>
opts?: Omit<UseFetchOptions<_ResT, Transform, PickKeys, Method>, 'lazy'>
): AsyncData<PickFrom<ReturnType<Transform>, PickKeys>, ErrorT | null>
export function useLazyFetch<
ResT = void,
ErrorT = FetchError,
ReqT extends NitroFetchRequest = NitroFetchRequest,
_ResT = ResT extends void ? FetchResult<ReqT> : ResT,
Method extends AvailableRouterMethod<ReqT> = 'get' extends AvailableRouterMethod<ReqT> ? 'get' : AvailableRouterMethod<ReqT>,
_ResT = ResT extends void ? FetchResult<ReqT, Method> : ResT,
Transform extends (res: _ResT) => any = (res: _ResT) => _ResT,
PickKeys extends KeyOfRes<Transform> = KeyOfRes<Transform>
> (
request: Ref<ReqT> | ReqT | (() => ReqT),
arg1?: string | Omit<UseFetchOptions<_ResT, Transform, PickKeys>, 'lazy'>,
arg1?: string | Omit<UseFetchOptions<_ResT, Transform, PickKeys, Method>, 'lazy'>,
arg2?: string
) {
const [opts, autoKey] = typeof arg1 === 'string' ? [{}, arg1] : [arg1, arg2]

View File

@ -17,6 +17,9 @@ describe('API routes', () => {
it('generates types for routes', () => {
expectTypeOf($fetch('/api/hello')).toEqualTypeOf<Promise<string>>()
expectTypeOf($fetch('/api/hey')).toEqualTypeOf<Promise<{ foo: string, baz: string }>>()
expectTypeOf($fetch('/api/hey', { method: 'get' })).toEqualTypeOf<Promise<{ foo: string, baz: string }>>()
// @ts-expect-error not a valid method
expectTypeOf($fetch('/api/hey', { method: 'patch ' })).toEqualTypeOf<Promise<{ foo: string, baz: string }>>()
expectTypeOf($fetch('/api/union')).toEqualTypeOf<Promise<{ type: 'a', foo: string } | { type: 'b', baz: string }>>()
expectTypeOf($fetch('/api/other')).toEqualTypeOf<Promise<unknown>>()
expectTypeOf($fetch<TestResponse>('/test')).toEqualTypeOf<Promise<TestResponse>>()
@ -49,6 +52,10 @@ describe('API routes', () => {
it('works with useFetch', () => {
expectTypeOf(useFetch('/api/hello').data).toEqualTypeOf<Ref<string | null>>()
expectTypeOf(useFetch('/api/hey').data).toEqualTypeOf<Ref<{ foo: string, baz: string } | null>>()
expectTypeOf(useFetch('/api/hey', { method: 'GET' }).data).toEqualTypeOf<Ref<{ foo: string, baz: string } | null>>()
expectTypeOf(useFetch('/api/hey', { method: 'get' }).data).toEqualTypeOf<Ref<{ foo: string, baz: string } | null>>()
// @ts-expect-error not a valid method
useFetch('/api/hey', { method: 'PATCH' })
expectTypeOf(useFetch('/api/hey', { pick: ['baz'] }).data).toEqualTypeOf<Ref<{ baz: string } | null>>()
expectTypeOf(useFetch('/api/union').data).toEqualTypeOf<Ref<{ type: 'a', foo: string } | { type: 'b', baz: string } | null>>()
expectTypeOf(useFetch('/api/union', { pick: ['type'] }).data).toEqualTypeOf<Ref<{ type: 'a' } | { type: 'b' } | null>>()