[TypeScript] 형지옥에서 싸우는 정규 노트.

여기.도 같은 글을 썼습니다

유니버설 전환의 필요성


TypeScript를 사용하여 몰드를 처리할 때는 지정된 데이터 구조를 재구성하여 새 구조를 만들고 값을 반환해야 합니다.만약 이것이 변하지 않는 구조라면, 물론 매번 수동으로 정의해도 문제없다.그러나 API를 만들 때 대응하는 도구가 자동으로 생성하는 유형은 매우 힘들다.이런 상황에서 유니버설 구조를 만들 수 있다면 자동으로 생성되는 유형에 대응할 수 있다.이때의 장르 정의 정석을 소개해 드리도록 하겠습니다.

Generics 및 Type


type의 일반적인 사용 방법을 아는 것으로 추진하다.Type에서 Generics를 사용하고 extends에서 유형 변환을 할 때 다음과 같이 쓴다type タイプ名<型変数 extends 制約,...> = 型変数 extends 条件 ? 真の型 : 偽の型 = 이전extends과 이후extends에서는 의미의 차이에 주의해야 한다.전자는 유형을 사용할 때 틀린 쪽이 들어갈 때 문법 오류가 발생한다.후자는 조건에 따라 반환자가 의견 차이를 일으키는 것이다.

Generics에 등장하는 infer

type タイプ名<型変数 extends 制約,...> = 型変数 extends 条件(infer 取り出し変数) ? 真の型(取り出した変数が使用可能) : 偽の型 infer 이후에 예를 들어 설명하지만 조건의 일부분으로 편입하면 이 부분만 추출할 수 있다

구체적 예


조건에 따라 분류하다

  • T는string이면number이고 이외에는boolean
  • 이다
    type Test01<T> = T extends string ? number : boolean;
    
    이것을 사용하면 다음과 같다.
    type Test01A = Test01<string>; //number
    type Test01B = Test01<object>; //boolean
    
  • T와 상관없이 질의응답 없이number(unknown도any)
  • 로 전환
    type Test02<T> = T extends unknown ? number : boolean;
    
    extends의 조건으로any와unknown(대상은never 이외)을 사용하면 조건을 강제로 성립시킬 수 있다.이를 사용하는 경우 기존 유형과 상관없이 구조 재편을 진행하는 경우다.
    사용 예는 다음과 같다.
    type Test02A = Test02<string>; //number
    type Test02B = Test02<object>; //number
    

    특정 부분 제거

  • 검증용 인터페이스 만들기
  • interface TestIF01 {
      a: { a0: number };
      b: { b0: number };
    }
    
  • infer의 이용과 부분 추출
  • type Test03<T> = T extends { a: infer R } ? R : never;
    
    대상에서 a의 유형을 추출하고 존재하지 않으면never로 되돌려줍니다
    type Test03A = Test03<TestIF01>; //{a0:number}
    type Test03B = Test03<{}>; //never
    
  • 키 삭제 및 값 추출
  • type Test04<T> = T extends { [_ in keyof T]: infer R } ? R : never;
    
    대상에서 키 부분을 꺼내려면 키of를 사용할 수 있지만,valueof와 같은 키워드는 존재하지 않기 때문에 위와 같이 설명해야 한다.
    객체 키를 결정하지 않고 컨텐트를 체크 아웃하는 경우{[キー変数 in keyof キー型]:値}이런 작법.
    type Test04A = Test04<TestIF01>; //{a0:number} | {b0:number}
    
    키를 지정하지 않고 객체 컨텐트를 체크 아웃하면 공통 쉐이프가 됩니다.결과가 교차형일 가능성도 있기 때문에 다음 항목에서 방법을 소개한다.

    공통 체형을 교차형으로 바꾸다

  • 변환 방법
  • type Test05<T> = (T extends any ? (_: T) => void : never) extends (
      _: infer R
    ) => void
      ? R
      : never;
    
    입력한 유형을 함수 매개 변수로 변환한 후 공용체형을 교차형으로 변환한다.
    type Test05A = Test05<Test04A>; //{a0:number} & {b0:number}
    
    {a0:number} | {b0:number}{a0:number} & {b0:number}로 전환하는 중간 과정은 다음과 같다.(_:{a0:number})=>void | (_:{b0:number})=>void단일 매개 변수가 아니라 함수는 공용 체형이다.
    아니오(_:{a0:number} | {b0:number})=>void,주의하세요.
    여기서 매개 변수의 유형을 선택하면 함수의 조건이 성립되는 것은 교차형의 대상이 되는 원리이다.빙빙 돌기는 했지만 조합 전환 기능이 없어 이렇게 쓸 수밖에 없었다.

    구조를 정돈하다

  • 검증용 인터페이스 만들기
  • interface TestIF02 {
      200: { token: string };
      500: { err: string };
    }
    
    위의 인터페이스는 RestAPI 관련 도구를 통해 자동으로 출력되는 간단한 예입니다.
  • 성형
  • type Test06<T> = T extends unknown
      ? { [M in keyof T]: { code: M; value: T[M] } } extends {
          [_ in keyof T]: infer R;
        }
        ? R
        : never
      : never;
    
    {code:コード,value:値}의 형식으로 변환
    type Test06A = Test06<TestIF02>; 
    	//{code:200,value:{token:string}} | {code:500,value:{err:string}}
    
    공용체형으로 이러한 구조를 만들면 코드 값을 판정할 때value 유형을 결정하는 논리를 사용할 수 있다
    if(result.code === 200){
    	result.value //{token:string}が確定
    }else{
    	result.value //{err:string}が確定
    }
    

    복잡화 유형 변환


    이번에 소개한 것은 type의 전환 예로 도대체 입문 수준일 뿐이라고 한다.Generics를 함수 매개 변수 등으로 설정하여 제한하거나 반환값 변환 등을 진행할 때 변환 절차가 커집니다.
    다음은 openapi-typescript의 패키지에서 토출된 RestAPI의 유형 데이터를 활용하여 매개변수와 반환 값을 생성한 예입니다.실제 동작은 그리 대단한 일은 아니지만 금형을 조립하는 작업은 대부분의 비용을 들인다.
    import { paths } from '@/types/api'
    
    const baseURL = process.env.NODE_ENV === 'development' ? 'http://localhost:4010' : '/api'
    
    export const requestApi = <
      T extends paths,
      path extends keyof T,
      method extends keyof T[path],
      body extends T[path][method] extends { parameters: { body: { [key: string]: infer R } } }
        ? R
        : never,
      response extends T[path][method] extends { responses: infer res }
        ? {
            [P in keyof res]: {
              code: P
              body: res[P] extends { schema: infer R } ? R : res[P]
            }
          } extends {
            [P in any]: infer R
          }
          ? R
          : never
        : never
    >(
      method: method,
      path: path,
      body?: body,
      token?: string
    ): Promise<response> => {
      return fetch(baseURL + path, {
        method: method as string | undefined,
        headers: {
          'Content-Type': 'application/json',
          ...(token ? { Authorization: `Bearer ${token}` } : {})
        },
        body: body && JSON.stringify(body)
      }).then(
        async (res) =>
          ({
            code: res.status,
            body: await res.json()
          } as response)
      )
    }
    

    총결산


    퍼즐 맞추는 거 재밌어요.

    좋은 웹페이지 즐겨찾기