Typescript를 사용하여 범용 유형 중 하나 구현

22460 단어 typescript
Typescript 코드 라이브러리에서 oneOf을 설명하려는 사례를 본 적이 있습니까?
나는 여러 번 했지만, 그 전에, 나는 어쩔 수 없이 가독성과 유형 안전성 사이에서 선택을 해야 했다😑.
예를 들어 우리는 이러한 사실을 표현하고 싶다. 우리의 게임 유저는 모험하기 전에 동물 한 마리를 선택하여 그와 동행할 수 있다.그러나 게임에서 사용할 수 있는 모든 동물은 아니다. 그는 다음 두 가지 동물 중 하나만 선택해야 한다.
  • 개 한 마리🐶
  • 늑대🐺
  • 매 한 마리🦅
  • a 마우스🐭
  • 유형 보안 가독성


    만약 우리가 유형의 안전성보다 가독성에 더 관심이 있다면, 우리는 간단하게 동물과 유저를 다음과 같이 묘사할 수 있다.
    interface Animal {
      id: string;
      name: string;
    
      // the animal can be ONE of the following
      dog: Dog | null;
      wolf: Wolf | null;
      eagle: Eagle | null;
      mouse: Mouse | null;
    }
    
    interface Player {
      id: string;
      nickname: string;
      level: number;
      animal: Animal;
    }
    
    이런 상황에서 인터페이스는 매우 쉽게 읽을 수 있으나, 우리는 플레이어에 dogwolf을 정의할 수 있으며, Typescript는 여전히 만족스럽다.

    형식 보안이 가독성을 초과하다


    우리는 선수들이 둘 사이에서만 선택할 수 있다는 것을 안다🐶, 🐺, 🦅 화목하다🐭. 우리는 이 정보를 사용할 수 있습니다. 주석 // the animal can be ONE of the following이 아니라 Typescript를 사용하여 이 점을 표현할 수 있습니다. (이것은 잠시 후에 우리에게 추가 유형의 안전을 가져다 줄 것입니다!)
    간단하게 말하자면, 우리는 단지 두 가지 측면에서 시작한다.🐶, 🐺.
    type Animal = {
      id: string;
      name: string;
    } & (
      | {
          dog: Dog | null;
          wolf: null;
        }
      | {
          dog: null;
          wolf: Wolf | null;
        });
    
    interface Player {
      // ... same ...
    }
    
    이런 방식을 통해 우리는 동물이 개나 늑대일 수 있지만 둘을 동시에 가질 수 없다는 사실을 확실히 나타냈다.위의 유형을 사용하는 것은 합리적인 것 같지만 이제 이 네 가지 동물의 외관을 살펴보자.
    type Animal = {
      id: string;
      name: string;
    } & ({
      dog: Dog | null;
      wolf: null;
      eagle: null;
      mouse: null;
    } | {
      dog:  null;
      wolf: Wolf | null;
      eagle: null;
      mouse: null;
    } | {
      dog: Dog null;
      wolf: Wolf null;
      eagle: Eagle | null;
      mouse: null;
    } | {
      dog: null;
      wolf: null;
      eagle: null;
      mouse: Mouse | null;
    });
    
    interface Player {
      // ... same ...
    }
    
    응😵... 지금 우리가 게임을 업데이트하면 5개, 10개 또는 15개의 새로운 동물을 추가하면 어떤 모습일지 상상할 수 있습니까?🤕
    이것은 근본적으로 확장할 수 없다.

    쌍방이 모두 좋게 하다


    얼마나 멋있는지
    type Animal = {
      id: string;
      name: string;
    } & OneOf<{
      dog: Dog;
      wolf: Wolf;
      eagle: Eagle;
      mouse: Mouse;
    }>;
    
    interface Player {
      // ... same ...
    }
    
    만약 우리가 이전의 예처럼 모든 유형의 안전성을 가지고 있다고 가정한다면, 나는 이것이 매우 좋을 것이라고 생각한다.
    그런데 저희가 할 수 있을까요?한 걸음 한 걸음 해 봅시다.
    먼저 해야 할 일은 우리가 그것을 전달하면
    {
      dog: Dog;
      wolf: Wolf;
      eagle: Eagle;
      mouse: Mouse;
    }
    
    위 유형에서 dog과 같은 키를 찾으면 다음 유형을 얻을 수 있습니다.
    {
      dog: Dog;
      wolf: Wolf | null;
      eagle: Eagle | null;
      mouse: Mouse | null;
    }
    
    우리는 이러한 유형을 OneOnly이라고 부른다.
    type OneOnly<Obj, Key extends keyof Obj> = { [key in Exclude<keyof Obj, Key>]: null } & Pick<Obj, Key>;
    
    이러한 유형을 이해하고 이해해 보겠습니다.
    type OneOnly<Obj, Key extends keyof Obj>
    
    아직까지는 괜찮을 거예요.우리는 Obj이라는 유형을 전달할 수 있으며, 이 유형의 키를 두 번째 매개 변수로 전달해야 한다.
    { [key in Exclude<keyof Obj, Key>]: null }
    
    현재, 우리는 일반적인 두 번째 매개 변수로 전달되는 키를 제외하고 Obj 유형의 모든 키를 훑어보고 있다.이 모든 키에 대해 우리는 그들이 받아들일 수 있는 유일한 값이 null이라고 말한다.
    퍼즐의 마지막 부분:
    Pick<Obj, Key>
    
    우리는 Obj 유형에서 Key을 추출했다.
    그래서 결국 저희가 얻었어요.
    type OneOnly<Obj, Key extends keyof Obj> = { [key in Exclude<keyof Obj, Key>]: null } & Pick<Obj, Key>;
    
    예를 들어, dog과 함께 사용하면 다음과 같은 결과를 얻을 수 있습니다.
    type OnlyDog = OneOnly<
      {
        dog: Dog;
        wolf: Wolf | null;
        eagle: Eagle | null;
        mouse: Mouse | null;
      },
      'dog'
    >;
    
    OnlyDog형은 다음과 같습니다.
    {
      dog: Dog;
      wolf: Wolf | null;
      eagle: Eagle | null;
      mouse: Mouse | null;
    }
    
    차갑다마지막 OneOf형 중 가장 복잡한 부분을 완성했다고 생각합니다.✅.
    지금, 너는 이미 보았을지도 몰라...Obj 유형을 반복해서 사용하고 키당 OneOnly 유형을 생성해야 합니다.
    type OneOfByKey<T> = { [key in keyof T]: OneOnly<T, key> };
    
    여기는 그리 화려한 것은 없지만, 이 직업은 매우 강하다!만약 우리가 이렇게 한다면
    type OnlyOneAnimal = OneOfByKey<{
      dog: Dog;
      wolf: Wolf;
      eagle: Eagle;
      mouse: Mouse;
    }>;
    
    다음과 같은 유형이 제공됩니다.
    {
      dog: OneOnly<Dog>;
      wolf: OneOnly<Wolf>;
      eagle: OneOnly<Eagle>;
      mouse: OneOnly<Mouse>;
    }
    
    그게 슈우퍼 쿨 맞나요?하지만🤔
    우리는 위 유형의 모든 값의 병합 유형이 필요하다.
    유사:
    OneOnly<Dog> | OneOnly<Wolf> | OneOnly<Eagle> | OneOnly<Mouse>
    
    우리는 어떻게 해야만 이 점을 할 수 있습니까?다음과 같은 몇 가지만 수행할 수 있습니다.
    type ValueOf<Obj> = Obj[keyof Obj];
    
    그러면 Obj 유형의 모든 값에 대한 결합 유형이 생성됩니다.
    마지막으로, 우리의 OneOf은 지금:
    type OneOfType<T> = ValueOf<OneOfByKey<T>>;
    
    🤩 좋아, 그렇지 않아?🤩
    다음은 전체 코드 요약입니다.
    type ValueOf<Obj> = Obj[keyof Obj];
    type OneOnly<Obj, Key extends keyof Obj> = { [key in Exclude<keyof Obj, Key>]: null } & Pick<Obj, Key>;
    type OneOfByKey<Obj> = { [key in keyof Obj]: OneOnly<Obj, key> };
    export type OneOfType<Obj> = ValueOf<OneOfByKey<Obj>>;
    
    그리고 우리가 지금 그것을 어떻게 사용하는지:
    type Animal = {
      id: string;
      name: string;
    } & OneOf<{
      dog: Dog;
      wolf: Wolf;
      eagle: Eagle;
      mouse: Mouse;
    }>;
    
    interface Player {
      // ... same ...
    }
    
    지금 가장 멋있는 일은 플레이어와 동물을 정의할 때 우리는 그 중의 한 동물만 정의할 수 있다는 것이다.0이 아니라 2가 아니라 3이 아니라 4가 아니다.오직 하나😁.
    이것은 Typescript Playground입니다. 당신은 즐겁게 놀 수 있습니다.이것은 우리가 본 모든 코드와 위gif에 표시된 최종 프레젠테이션을 포함한다.

    맞춤법 오류 발견?


    이 블로그 글에서 맞춤법 오류, 개선할 수 있는 문장, 업데이트해야 할 내용을 발견하면git 저장소를 통해 접근해서 요청할 수 있습니다.의견을 발표하지 말고 바로 이동하여 새로운 추출 요청과 변경 사항을 열어 주십시오.

    좋은 웹페이지 즐겨찾기