개발/타입스크립트 챌린지

[TSCH][타입스크립트 챌린지](4) Readonly 구현

Junghyun Kim 2022. 4. 3. 23:04
반응형

오늘의 문제 - Readonly 2

Readonly 유틸리티 타입 또한 타입스크립트 유저들이 많이들 사용하는 타입이다.
오늘은 이 타입 구현 도전이다.

먼저 아래 주소를 통해 한 번 도전하는 것을 권한다.

https://tsch.js.org/4/play

 

TS Playground - An online editor for exploring TypeScript and JavaScript

The Playground lets you write TypeScript or JavaScript online in a safe and sharable way.

www.typescriptlang.org

 

타입스크립트 플레이 그라운드


문제 분석

type MyReadonly2<T, K> 타입을 구현하면된다.
K는 T의 프로퍼티 중에서 readonly로 만들 프로퍼티들의 집합이다.

cases에 나오는 것처럼 K가 명시되어 있지않다면, T의 전체 프로퍼티를 readonly로 만들면 된다.


솔루션 도전 과정

먼저 첫번째로 생각한 부분은 지난 번에 배운 as 키워드를 이용하여 K 집합에 속하면 readonly 키워드를 붙이고
그렇지 않으면 never로 처리하려고 하였다.

type MyReadonly2<T, K> = {
  readonly [key in keyof T as key extends K ? key : never]: T[key]
  [key2 in keyof T as key2 extends K ? never : key2]: T[key2]
}

그러나 아래처럼 T, key2 에 빨간 아랫줄이 그어졌다.

T, key2를 찾을 수 없다는 에러가 뜬다.

이를 통해 알 수 있는 것은 타입에서는 단 하나의 Mapped Types를 사용할 수 있다는 것이다.

그래서 코드를 아래와 같이 수정했다. 
두개의 집합을 이용한 코드이다.

type MyReadonly2<T, K> = {
  readonly [key in keyof T as key extends K ? key : never]: T[key]
} & {
  [key in keyof T as key extends K ? never : key]: T[key]
}

2,3번째 케이스 통과

위 코드를 통해 2,3번째 케이스가 통과됐음을 확인할 수 있었다.
이제 남은 것은 K가 들어오지 않을 때는 모든 프로퍼티를 readonly로 만드는 것이다.

... 그러나 여기서 막혔다.
저 K가 들어오지 않을 때 처리하는 방법이 도저히 떠오르지 않았다.
아래와 같이 요상한 코드들도 시험삼아 작성해봤는데 이상할 뿐이다.

type MyReadonly2<T, K = never> = K extends never ? Readonly<T> : MyReadonly3<T, K>

type MyReadonly3<T, K> = {
    readonly [key in keyof T as key extends K ? key : never]: T[key]
  } & {
    [key in keyof T as key extends K ? never : key]: T[key]
  }

솔루션

결국 타인들이 어떻게 작성했는지 확인해보았다.
가장 코멘트가 많이 달린 기준으로 정렬해서 나온 첫번째 솔루션을 확인해봤다.

일단 underfin 유저가 작성한 솔루션이 굉장히 간단하고 직관적으로 보였다.
따봉 수도 많이 있어서 그대로 따라해보았다.

풀리지 않는 underfin의 솔루션

근데 이상하게도 답이 되지않았다.
아마 그 사이에 cases와 일치하는지 파악하는 메커니즘이 바뀐듯하다.
(코멘트를 보면 현재는 Alike를 사용하지만 당시에는 Equal을 사용한 것을 확인할 수 있다. https://github.com/type-challenges/type-challenges/issues/6#issuecomment-900188034)

결국 antfu 유저가 작성한 솔루션을 한번 해석해보려한다.

antfu 유저의 솔루션

먼저 Diff 타입은 지난 시간에 확인한 Exclude 타입과 똑같다.
차집합의 의미를 지닌 타입이다.
MyReadonly2의 구현을 보면 나와 같이 교집합을 통해 구현을 함을 확인할 수 있었다.
먼저 찻번째 집합은 K 집합에 속한 프로퍼티들을 readonly로 만들어주고,
두번째 집합에서는 T의 프로퍼티 집합에서 K 집합을 제외한 프로퍼티들을 변함없이 그대로 사용하는 것을 확인할 수 있었다.

문제는 K의 디폴트 값을 집어넣는 부분인데 생각보다 굉장히 간단했다.
K = keyof T 라고만 적어주면 되었다.
이렇게 하나 또 배워간다.

위 솔루션을 참고하여 내가 만든 솔루션은 아래와 같다.

type MyReadonly2<T, K = keyof T> = {
  readonly [key in keyof T as key extends K ? key : never]: T[key]
} & {
  [key in keyof T as key extends K ? never : key]: T[key]
}

솔루션 성공

 

반응형