구보현 블로그

[TypeScript]Type Challenges - Pick, Readonly, Tuple of Object

20220319

타입스크립트를 잘 써보고 싶기도 하고 재밌어 보여서 type-challenges를 시작했습니다.
이번 글에서는 type challenges easy 단계인 Pick, Readonly, Tuple of Object 과제를 풀어보고 정리해보았습니다 ~

Pick 구현하기

  • Pick<T, K>
interface Todo {
  title: string
  description: string
  completed: boolean
}

type TodoPrevidew = MyPick<Todo, 'title' | 'completed'>
const todo: TodoPrivew = {
  title: 'Celan room', 
  completed: false,
}

solution

type MyPick<T, K extends keyof T> = {
  [P in K]: T[P]
}
  • <T, K extends keyof T>
    • 어떤 타입을 받아오고, K라는 것은 T 타입에 있는 키들을 상속한 것들이다.
    • 항상 Pick 을 할 때는 기존 T에 있는 키를 써야 함
  • 전달된 K들에 한해서만 빙글빙글 돌면서 타입 지정 매핑

Readonly 구현하기

  • Readonly
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

solution

type MyReadonly<T> = {
 readonly [P in keyof T]: T[P]
}
  • P는 T타입의 모든 키들 중에 하나다.
    • [P in keyof T]
  • Index Type
    • T[P] Index Type 인덱스를 기준으로 타입을 결정할 수 있다.

Tuple of Object

const tuple = ['tesla', 'model 3', 'model X', 'model Y'] as const

type result = TupleToObject<typeof tuple> // expected { tesla: 'tesla', 'model 3': 'model 3', 'model X': 'model X', 'model Y': 'model Y'}

solution

type TupleToObject<T extends readonly any[]> = {
  [P in T[number]] : P
}
type tuple = readonly ['tesla', 'model 3', 'model X', 'model Y']
  • readonly 키워드를 명시적으로 수식한 이유는 어떤 인덱스에 어떤 자료가 들어있는지 기술한 것과 같아서 readonly와 마찬가지다.
  • 즉 tuple 타입인 값을 선언할 때 해당 자리에 다른 값이 들어갈 수 없게 된다.
const tuple = ['tesla', 'model 3', 'model X', 'model Y'] as const
  • 문제처럼 as const 를 써주면 값 정의도하고, tuple 타입을 가져올 수도 있다.
  • 이렇게 정의한 튜플 타입의 값은 몇 가지 고유한 성질을 가진다.
    1. Array 인터페이스를 만족한다.
    2. 인덱스 타입도 불변이며, 컴파일러가 그 정확한 값을 기억하고 있다.
  • T[number] 이렇게 배열 요소의 타입을 참조할 수 있다.
// indexed Access Types 

const MyArray = [
  {name: 'Alice', age: 15}, 
  {name: 'Bob', age: 23},
  {name: 'Eve', age: 38}, 
]

type Person = typeof MyArray[number];
type Age = typeof MyArray[number]['age'];

type Age2 = Person['age'];

Mapped Type?

  • Mppaed 타입을 더 알아보자면...
  • 이미 선언된 타입의 프로퍼티에서 새로운 타입을 만드는 것(Pick, Readonly..)
type OnlyBoolsAndHores = {
  [key: string]: boolean | Horse
}

매핑된 타입은 PropertyKey(keyof를 통해서 생성되는)의 조합을 사용하여 키를 통해 타입을 반복적으로 생성하는 제너릭 타입이다.


type OptionsFlags<Type> = {
  [Property in keyof Type]: boolean;
}
type CreateMutable<Type> = {
  - readonly [Property in keyof Type]: Type[Property];
}

type LockedAccount = {
  readonly id: string;
  readonly name: string;
}

type UnlockedAccount = createMutable<LockedAccount>;

optional 속성을 제거할 수도 있다.

type Concrete<Type> = {
  [Property in kyeof Type]-?: Type[Property];
}

회고

회사에서 유지보수나 단순 리팩터링 작업만 하고 있어서 딱히 타입을 복잡하게 사용할 일이 별로 없었다.
하지만 나는 잘 모르기 때문에... 내가 잘 몰라서 안 쓰는 건지 쓸 필요가 없어서 안 쓰는 건지 잘 모른다... 이제부터 알아봐야지...ㅎ
문제는 Easy 단계인데 어려웠다. Readonly나 Pick 같은 건 이전에 한번 해본 적이 있어서 괜찮았는데 마지막 문제는 너무 어려웠다.
나는 모르는게 너무 많구나...😇

참고자료

https://blog.cometkim.kr/posts/typescript-tuples/

https://www.typescriptlang.org/ko/docs/handbook/2/mapped-types.html#key-remapping-via-as

https://www.typescriptlang.org/docs/handbook/2/indexed-access-types.html