Nuxt3의useFetch의 장르 정의를 탐색해보면 재밌을 것 같아요.

이 글은 개인 블로그의 내용과 같다
https://kotamat.com/post/ec36414b696c12/
며칠 전 10월 12일 Nuxt3가 Public beta가 되었습니다!🎉
Nuxt2에서 철저히 변경된 Nuxt3는 흥미로운 변경점이 많은데, 이번에는 useFetch의 행동에 대한 탐색을 진행한다.

이른바 useFetch?


Data Fetching에 설명된 비동기식 데이터 획득 API 중 하나입니다.
useFetch(url: string, options?)
과 같은 형식으로 호출된 매우 간단한 API이지만 useAsyncData$fetch의 잠금 메모리를 제공하고 자동으로 생성된 로컬 API의 응답 유형을 제공하기 때문에 유형 정의가 상당히 복잡해졌다.

먼저 유형 정의부터 시작합니다.


useFetch의 유형 정의는 다음과 같습니다.
export declare function useFetch<ReqT extends string = string, ResT = FetchResult<ReqT>, Transform extends (res: ResT) => any = (res: ResT) => ResT, PickKeys extends KeyOfRes<Transform> = KeyOfRes<Transform>>(url: ReqT, opts?: UseFetchOptions<ResT, Transform, PickKeys>): import("./asyncData").AsyncData<import("./asyncData").PickFrom<ReturnType<Transform>, PickKeys>>;
퍼즐감이 대단한데...
여기서부터 하나씩 풀어볼게요.

Transform 벗기기

TransformuseAsyncData의 옵션 중 하나입니다. 반환값의 변환기이기 때문에 먼저 깎아서 간단하게 하겠습니다.
export declare function _useFetch<ReqT extends string = string, ResT = FetchResult<ReqT>, PickKeys = KeysOf<ResT>>(url: ReqT, opts?: UseFetchOptions<ResT, (input: ResT)=> ResT, PickKeys>): import("./asyncData").AsyncData<import("./asyncData").PickFrom<ResT, PickKeys>>;
※ 이 경우 UseFetchOptions의 제3형 매개 변수는 오류가 발생하지만, 의도적으로 유형의 추상도를 바꾸었기 때문에 잠시 무시

반환 값 주의


이곳에서 귀환치를 주목해 보자.asyncData.d.ts에서 다음과 같이 정의한다.
// Tのうち、Kの配列の要素に指定されたkeyの要素だけを抽出して取り出す
export declare type PickFrom<T, K extends Array<string>> = T extends Record<string, any> ? Pick<T, K[number]> : T;
...
// Dataの型を、asyncDataが返してくれる型に変換する
export interface _AsyncData<DataT> {
    data: Ref<DataT>;
    pending: Ref<boolean>;
    refresh: (force?: boolean) => Promise<void>;
    error?: any;
}
export declare type AsyncData<Data> = _AsyncData<Data> & Promise<_AsyncData<Data>>;
반환값은'ResT에서 PickKeys로 지정된 요소만 가져와 asyncData 반환 형식으로 전환하는 유형'이다.

Rest 검색


그럼 ResT도 탐색해 보세요.
ResT = FetchResult<ReqT>;
export declare type Awaited<T> = T extends Promise<infer U> ? U : T;
export declare type FetchResult<ReqT extends string> = Awaited<ReturnType<$Fetch<unknown, ReqT>>>;
이기 때문에 "ResT$Fetch<unknown, ReqT>의 반환치를 벗긴 프로미스 또는 자신이다."

$Fetch의 탐색

$Fetch는 다음과 같다.
export declare interface $Fetch<T = unknown, R extends FetchRequest = FetchRequest> {
  (request: R, opts?: FetchOptions): Promise<TypedInternalResponse<R, T>>
  raw (request: R, opts?: FetchOptions): Promise<FetchResponse<TypedInternalResponse<R, T>>>
}
ResT 컴퓨팅 의합ResT에 필요한 정보만 남는 경우
export declare interface $Fetch<unknown, ReqT> {
  (request: R, opts?: FetchOptions): Promise<TypedInternalResponse<ReqT, unknown>>
}
.TypedInternalResponse는 다음과 같다.
export declare type TypedInternalResponse<Route, Default> =
  Default extends string | boolean | number | null | void | object
    // Allow user overrides
    ? Default
    : Route extends string
      ? MiddlewareOf<Route> extends never
        // Bail if only types are Error or void (for example, from middleware)
        ? Default
        : MiddlewareOf<Route>
      : Default
이번 모델로 바꾸면
export declare type TypedInternalResponse<ReqT, unknown> =
    ReqT extends string
      ? MiddlewareOf<ReqT> extends never
        // Bail if only types are Error or void (for example, from middleware)
        ? unknown
        : MiddlewareOf<ReqT>
      : unknown
.네버MiddlewareOf<ReqT>를 무시하면 답장이 온다고 한다.
나머지 유형 정의는 다음과 같다.
적용
export declare interface InternalApi { }

export declare type ValueOf<C> = C extends Record<any, any> ? C[keyof C] : never

export declare type MatchedRoutes<Route extends string> = ValueOf<{
  // exact match, prefix match or root middleware
  [key in keyof InternalApi]: Route extends key | `${key}/${string}` | '/' ? key : never
}>

export declare type MiddlewareOf<Route extends string> = Exclude<InternalApi[MatchedRoutes<Route>], Error | void>
MiddlewareOf<ReqT>해보면
export declare interface InternalApi { }

export declare type ValueOf<C> = C extends Record<any, any> ? C[keyof C] : never

export declare type MatchedRoutes = ValueOf<{
  // exact match, prefix match or root middleware
  [key in keyof InternalApi]: ReqT extends key | `${key}/${string}` | '/' ? key : never
}>

export declare type MiddlewareOf = Exclude<InternalApi[MatchedRoutes<ReqT>], Error | void>
.대략적으로'InternalApiReqT가 관건이 되는 것이 있다면 그 밸류(Value)를 반납하라'는 의미로 해석된다.
즉'ResTInternalApi의 키가 ReqT의 밸류에 해당하는 유형'이라고 추정할 수 있다.

인터넷 Api 자동 생성


상기 유형 정의InternalApi는 기본값{}이다.이대로 가면 아무런 의미가 없다는 얘기다.
이 유형의 정의를 완성할 수 있는 것은 Nuxt3의 서버 엔진nitro이다.nitro에는 다양한 기능이 있는데 그 중 하나/server/ 디렉터리에 설정된 함수의 반환값을 설명하고 형식 정의를 생성한다.
예를 들어, 다음 TS 파일/server/api/count.ts을 설정합니다.
let counter = 0;
export default (): { counter: number } => {
  counter++;
  return { counter };
};
다음 내용의 파일을 생성.nuxt/nitro.d.ts.
declare module '@nuxt/nitro' {
  interface InternalApi {
    '/api/count': ReturnType<typeof import('../server/api/count').default>
  }
}
export {}
InternalApi가 확장된 후 /api/count에 대한 유형 정의가 나타났다.
따라서 "Value의 유형ResTInternalApi에 대응하는ReqT"은
"ResTReqT/api/count일 때/server/api/count의 반환값이다."

useFetch의 반환값


이 효과를 탐색하기 위해useFetch를 실제로 사용합니다.
const {data} = await useFetch('/api/count')
data의 유형은 간단해야 한다
Ref<Pick<{
    counter: number;
}, "counter">>
.
useFetch에서 단점을 지정한 것에 불과하지만 그 반환값은 Vue3에서 쉽게 사용할 수 있는 유형으로 추출된 것으로 보인다.InternalApi의 디자인은 유형 정의가 없어도 사용할 수 있는 것으로 useFetch의 매개 변수의 보완이 작용하지 않는 것이 어려운 점이지만 Nuxt의 디렉터리 구조를 검색하면 파일 이름만 어느 정도 예측할 수 있어 편리하게 사용할 수 있다.

아무튼 일단 정리를 해볼게요.


다만 useFetch를 검색하면 되돌아오는 값이 상당히 길어서 잠시 여기서 끝냅니다.
이 외에도 useAsyncData$fetch 옵션이 지원됩니다. 가능하면 검색해 보세요.옵션의 지원은 위의 것과 비교하면 그리 대단한 일이 아니기 때문에 걱정하지 않아도 된다

좋은 웹페이지 즐겨찾기