본문 바로가기

FE/type-challenge

12. Chainable

728x90

문제

체인 가능 옵션은 일반적으로 Javascript에서 사용됩니다. 하지만 TypeScript로 전환하면 제대로 구현할 수 있나요?

이 챌린지에서는 option(key, value)get() 두가지 함수를 제공하는 객체(또는 클래스) 타입을 구현해야 합니다. 현재 타입을 option으로 지정된 키와 값으로 확장할 수 있고 get으로 최종 결과를 가져올 수 있어야 합니다.

declare const config: Chainable

const result = config
  .option('foo', 123)
  .option('name', 'type-challenges')
  .option('bar', { value: 'Hello World' })
  .get()

// 결과는 다음과 같습니다:
interface Result {
  foo: number
  name: string
  bar: {
    value: string
  }
}

cases

declare const a: Chainable

const result1 = a
  .option('foo', 123)
  .option('bar', { value: 'Hello World' })
  .option('name', 'type-challenges')
  .get()

const result2 = a
  .option('name', 'another name')
  // @ts-expect-error
  .option('name', 'last name')
  .get()

const result3 = a
  .option('name', 'another name')
  .option('name', 123)
  .get()

type cases = [
  Expect<Alike<typeof result1, Expected1>>,
  Expect<Alike<typeof result2, Expected2>>,
  Expect<Alike<typeof result3, Expected3>>,
]

type Expected1 = {
  foo: number
  bar: {
    value: string
  }
  name: string
}

type Expected2 = {
  name: string
}

type Expected3 = {
  name: number
}

문제 링크

정답

type Chainable<T = {}> = {
  option<U extends string, V>(
    key: U extends keyof T
      ? V extends T[U]
        ? never
        : U
      : U,
    value: V): Chainable<Omit<T, U> & Record<U, V>>
  get(): T
}

풀이

조건

  1. option 함수로 두 인자 key, value의 타입을 추가할 수 있어야 한다.
  1. get 으로 지금까지의 옵션들이 포함된 object를 출력할 수 있어야 한다.

해설

우선, get으로 현재까지 object를 출력하기 위해서 제네릭을 추가한다.

type Chainable<T = {}> = {
  option(key: string, value: any): any
  get(): T
}

option은 chaining 할 수 있어야 하므로 Chainable을 출력하도록 한다.

type Chainable<T = {}> = {
  option(key: string, value: any): Chainable<T>
  get(): T
}

출력 시 key와 value가 반영되도록 수정한다. 이 때, key와 value의 타입이 반영되도록 제네릭을 추가한다.

type Chainable<T = {}> = {
  option<U extends string, V>(key: U, value: V): Chainable<T & Record<U, V>>
  get(): T
}

동일한 key가 들어왔을 때 오류가 발생하므로 이를 제거하는 코드를 추가한다.

type Chainable<T = {}> = {
  option<U extends string, V>(key: U, value: V): Chainable<Omit<T, U> & Record<U, V>>
  get(): T
}

정상적으로 동작하지만, 2번째 케이스에서 알 수 있듯 입력시 동일한 key, 동일한 타입의 value가 들어올 때 에러가 발생할 수 있어야하므로, U를 검사하는 로직을 추가한다.

type Chainable<T = {}> = {
  option<U extends string, V>(
    key: U extends keyof T
      ? V extends T[U]
        ? never
        : U
      : U,
    value: V): Chainable<Omit<T, U> & Record<U, V>>
  get(): T
}

 


혹시 오류나 개선점이 있다면 댓글 부탁드립니다.

728x90

'FE > type-challenge' 카테고리의 다른 글

9286. FirstUniqueCharIndex  (0) 2023.09.07
9142. CheckRepeatedChars  (0) 2023.09.06
10. TupleToUnion  (0) 2023.09.04
9. DeepReadonly  (1) 2023.09.02
8. Readonly2  (0) 2023.09.01