타입 챌린지 5821 - MapTypes

소파의 벨로그·2025년 5월 6일

타입챌린지

목록 보기
75/131

문제 링크

문제

객체 T에 있는 타입을 주어진 형태의 R 타입에 의해 정의된 다른 타입으로 바꾸는 MapTypes<T, R>를 구현하라.

Implement MapTypes<T, R> which will transform types in object T to different types defined by type R which has the following structure.

 type StringToNumber = {
   mapFrom: string; // value of key which value is string
   mapTo: number; // will be transformed for number
 }

내 풀이

type Parser<T,R extends {mapFrom:any,mapTo:any},U extends {mapFrom:any,mapTo:any}=R>=
  T extends U['mapFrom']?
      R extends U?
        T extends R['mapFrom']?
          R['mapTo']
        :never 
      :never
    :T

type MapTypes<T extends object, R extends {mapFrom:any,mapTo:any}> =    
      {[K in keyof T]:Parser<T[K],R>}

우선 mapped 타입으로 순회하며 Parser에 넣은 값을 밸류 타입에 넣는다

Parser는 다음과 같이 동작한다.

1)T가 R에 있는 모든 mapFrom을 확장할 수 없으면 T를 그대로 반환
2)R을 분배법칙으로 분배
3)T가 분배된 R의 mapFrom을 확장가능하면 R의 mapTo, 아니면 never를 반환

3)의 결과로 T가 확장가능한 mapFrom들의 mapTo가 유니온 타입으로 나오게 된다.

이렇게 구현한 이유는 문제에서 R이 유니온 타입으로 들어올 때,
전체 객체의 union 타입이 아닌 바꾸는 값(mapFrom/mapTo)만 union 타입으로 반환하는 것을 요구했기 때문이다.

다른 사람의 풀이

type MapTypes<T, R extends { mapFrom: any; mapTo: any }> = {
  [K in keyof T]: T[K] extends R['mapFrom']
  ? R extends { mapFrom: T[K] }
    ? R['mapTo']
    : never
  : T[K]
}

Parser를 따로 구현하지 않고, 한 곳에 구현한 풀이이다.

다만, T[K]가 R을 확장하는 것이 아닌, R이 T[K]를 확장할 때 mapTo로 바꿔주는 방식이다.

그래서, 다음과 같이 T에 리터럴, R에 원 타입이 오는 것은 대응하지 못한다.

type test=MapTypes<{ stringToNumber: '1' }, { mapFrom: string, mapTo: number }>,

내 풀이에서는 test가 { stringToNumber : number}가 되고,
이 풀이에서는 test가 never가 된다.('1'은 string을 확장할 수 있지만, string은 '1'을 확장할 수 없기 때문

참고자료

https://github.com/type-challenges/type-challenges/issues/14152

0개의 댓글