본문 바로가기
Language/Typescript

타입챌린지 : 62-Type Lookup

by hsloth 2023. 3. 24.

 

이 글은 제가 타입챌린지를 하면서 해석한 내용을 적는 글입니다. 틀린 내용이 있으면 댓글 달아주시면 감사하겠습니다.

 

https://github.com/type-challenges/type-challenges/blob/main/questions/00062-medium-type-lookup/README.md

 

GitHub - type-challenges/type-challenges: Collection of TypeScript type challenges with online judge

Collection of TypeScript type challenges with online judge - GitHub - type-challenges/type-challenges: Collection of TypeScript type challenges with online judge

github.com

 

LookUp의 제네릭에 들어가는 타입들 중 type속성을 기준으로 하나를 골라서 반환하는 타입이다.

 

type LookUp<U, T> = U extends {type:T} ? U : never;

/* _____________ Test Cases _____________ */
import type { Equal, Expect } from '@type-challenges/utils'

interface Cat {
  type: 'cat'
  breeds: 'Abyssinian' | 'Shorthair' | 'Curl' | 'Bengal'
}

interface Dog {
  type: 'dog'
  breeds: 'Hound' | 'Brittany' | 'Bulldog' | 'Boxer'
  color: 'brown' | 'white' | 'black'
}

type Animal = Cat | Dog

type cases = [
  Expect<Equal<LookUp<Animal, 'dog'>, Dog>>,
  Expect<Equal<LookUp<Animal, 'cat'>, Cat>>,
]

 

일단 단순하게 생각해보자. 간단하게 생각하면, U에 T가 들어있으면 U를 리턴하는 타입이니 다음과 같이 짜보자.

type LookUp<U, T> = U extends T ? U : never;

당연이 이 답은 오답이다. 하지만 이렇게 하나 둘 틀을 잡아가 보자.

type LookUp<U, T> = U extends T ? U : never; 에서 T는 그저 key의 value값을 나타내므로, U에 type이 T를 가진다면 으로 문장을 바꿀 필요가 있다.

따라서 다음과 같이 바꿔보자.

type LookUp<U extends {type: string}, T> = U['type'] extends T ? U : never;

type LookUp<U extends {type: string}, T> : U가 type이라는 key를 받도록 설정해주었다.

 

U['type'] extends T ? U : never : U의 type속성을 꺼내서, 해당 속성이 T값이면 U를 리턴. 아니면 never를 리턴하도록 했다.

그런데... 에러가 뜬다. 왜일까?

그래서 나름대로 추측해 보았는데, T에 as const를 추가해서 타입으로 확정지어서 값을 넣어주지 않는 이상, T는 컴파일시 string으로만 추론되어 결국 U['type'] === string 이런식으로 되어 에러가 나는 것 같다.

타입 레벨에서 코딩하는건 조심할 필요가 있다...

 

T는 string으로 추론이 될테니... 다음과 같이 바꾸어 주었다.

type LookUp<U, T> = U extends {type:T} ? U : never;

이러면 에러가 발생하지 않는다!

type LookUp<U, T> : U와 T를 제네릭 인자로 받는 LookUp타입을 정의한다.

U extends {type: T} ? U : never : U가 type:T를 속성으로 가지고 있으면 U를 리턴하고 아니면 never를 리턴한다.