개발자 메론빵

[Type Challenges] Pick, Readonly 본문

TypeScript

[Type Challenges] Pick, Readonly

ddiae 2024. 10. 17. 09:02

📌Pick

Q. T에서 K프로퍼티만 선택해 새로운 오브젝트 타입을 만드는 내장 제네릭 Pick<T, K>를 이를 사용하지 않고 구현하세요.
interface Todo {
  title: string
  description: string
  completed: boolean
}

type TodoPreview = MyPick<Todo, 'title' | 'completed'>

const todo: TodoPreview = {
    title: 'Clean room',
    completed: false,
}

Pick을 사용하지 않고 내장 제네릭 Pick을 구현하는 문제이다. 

우선 문제를 풀기 전 Pick 타입에 대해 확인해보고자 한다. Pick 타입이란 기존 인터페이스나 객체 타입에서 필요한 속성들만 선택적으로 사용할 수 있도록 한다. Pick<T, K>일 때 T는 원본 타입, K는 선택할 속성들의 Key 집합에 해당한다. 예를 들어 위 문제에서 아래와같이 type TodoTest를 정의한다고 가정해본다.

type TodoTest = Pick<Todo, 'title' | 'completed'>

위와 같이 TodoTest를 정의했을 때, todo의 타입을 TodoTest라고 정의할 수 있는 것이다.

이렇게 Pick을 사용하여 반복적인 타입 선언을 방지할 수 있다.

 

하지만, Pick을 위 문제를 해결하기 위해서는 type MyPick을 정의해야 한다.

K extends keyof T 형태로 T의 key를 확장하는 K를 선언해준 뒤 Mapped type을 활용하여 type MyPick을 아래와 같이 정의될 수 있다.

type MyPick<T, K extends keyof T> = { [P in K] : T[P]}

+) Mapped type?

특정 타입의 속성을 변환하여 새로운 타입을 만든다. 주어진 타입의 모든 키를 반복하면서 그 키와 관련된 값을 수정하므로 map() 메서드와 유사한 기능을 하며 타입을 만든다고 볼 수 있다. 중복되는 타입 선언을 방지할 수 있다. Pick 유틸 타입도 Mapped Type을 바탕으로 제작된 것이다.

 


📌Readonly

Q. T의 모든 프로퍼티를 읽기 전용(재할당 불가)으로 바꾸는 내장 제네릭 'Readonly<T>'를 이를 사용하지 않고 구현하세요.

interface Todo {
  title: string
  description: string
}

const todo: MyReadonly<Todo> = {
  title: "Hey",
  description: "foobar"
}

todo.title = "Hello" // Error: cannot reassign a readonly property
todo.description = "barFoo" // Error: cannot reassign a readonly property

앞선 Pick 문제에서 사용한 것과 동일하게 Mapped Type을 사용하여 Readonly<T>를 다음과 같이 구현할 수 있다. 

type MyReadonly<T> = { readonly [P in keyof T] : T[P] }