✍ 공부/TypeScript

[type-challenges] Tuple to Object

Po_tta_tt0 2023. 5. 16. 16:42
반응형

 

 

 

 

0. 문제

배열이 주어지면, 이를 객체로 변경하세요(key와 value는 제공된 배열에 존재해야 합니다)

 

 

 

 

1. 설명


/* _____________ Your Code Here _____________ */

type TupleToObject<T extends readonly (string|number)[]> = {[P in T[number]]: P}

type MyType = TupleToObject<typeof tupleMix> // 확인용

/* _____________ Test Cases _____________ */
import type { Equal, Expect } from '@type-challenges/utils'

const tuple = ['tesla', 'model 3', 'model X', 'model Y'] as const
const tupleNumber = [1, 2, 3, 4] as const
const tupleMix = [1, '2', 3, '4'] as const

type cases = [
  Expect<Equal<TupleToObject<typeof tuple>, { tesla: 'tesla'; 'model 3': 'model 3'; 'model X': 'model X'; 'model Y': 'model Y' }>>,
  Expect<Equal<TupleToObject<typeof tupleNumber>, { 1: 1; 2: 2; 3: 3; 4: 4 }>>,
  Expect<Equal<TupleToObject<typeof tupleMix>, { 1: 1; '2': '2'; 3: 3; '4': '4' }>>,
]

// @ts-expect-error
type error = TupleToObject<[[1, 2], {}]>
  • typeof tuple이란 ? readonly ["tesla", "model 3", "model X", "model Y"]이다.
  • 이 typeof tuple을 TupleToObject에 주입한다
  • 들어온 T는 readonly (string|number)[] -> type은 string인 것도 있고 number인 것도 있기 때문.
  • cases 안에 들어있는 string, number, string과 number가 섞여있는 tuple 모두 통과한다.
  • 빈 object는 string | number가 아니기 때문에 error가 발생한다

 

 

 

추가설명

1.

type TupleToObject<T extends readonly number[]> = {[P in T[number]]: P}

를 사용하면 tupleNumber만 통과한다고 생각했다 -> 맞다

type TupleToObject<T extends readonly string[]> = {[P in T[number]]: P}

을 사용하면 tuple만 통과한다고 생각했다 -> 이 또한 맞다

type TupleToObject<T extends readonly string[]| number[]> = {[P in T[number]]: P}

을 사용하면 tupleNumber와 tuple 두개는 통과하고, tupleMix는 통과하지 못할 것이라 생각했다 -> 또잉? tuple만 통과하고 나머지는 통과하지 못했다.

type TupleToObject<T extends readonly number[]| string[]> = {[P in T[number]]: P}

은 tupleNumber만 통과하고 나머지는 통과하지 못했다.

type TupleToObject<T extends  readonly number[] |  string[] > = {[P in T[number]]: P}

Type 'readonly ["tesla", "model 3", "model X", "model Y"]' does not satisfy the constraint 'string[] | readonly number[]'.
Type 'readonly ["tesla", "model 3", "model X", "model Y"]' is not assignable to type 'readonly number[]'.
Type 'string' is not assignable to type 'number'.

 

readonly arr type이 string[] | readonly number[]을 만족시키지 못한다는 것. 뭔가 감이 잡힌다.
둘 다 readonly를 빼보자

type TupleToObject<T extends number[] |  string[] > = {[P in T[number]]: P}

다 에러가 생긴다.
답이 나왔다! readonly로 전해준 type에서(typeof tuple이란 ? readonly ["tesla", "model 3", "model X", "model Y"]이다.) readonly를 내 나름대로 제외하려고 하니 에러가 생긴 것. 그러면 두개 다 readonly를 추가해주면 마지막 tupleMix만 빼고 통과할 것이다
-> 정답이다!

 

그러면 tupleMix또한 통과시키기 위해 새로운 readonly type을 추가해보자

type TupleToObject<T extends  readonly number[] | readonly string[] | readonly (string|number)[] > = {[P in T[number]]: P}

통과한다!

 

이걸 짧게 줄여 쓰면

type TupleToObject<T extends readonly (string|number)[]> = {[P in T[number]]: P}

가 될 수 있다.

 

그런데 왜 number이지?

const tupleRandomNumber = ['10', 2, 3, 4] as const

  Expect<Equal<TupleToObject<typeof tupleRandomNumber>, { 10: '10'; 2: 2; 3: 3; 4: 4 }>>,

 

뒤에서 쓰는 number가 index라고 예상은 했는데 눈으로 확인하고 싶어졌다.

type index = number
type TupleToObject<T extends Readonly<(number|string)[]> > = {[P in T[index]]: P}

좌항에서 나오는 number와는 완전히 다른 number가 쓰이고 있었다.
그냥 T의 index를 하나하나 돌면서 뽑아낸다는 의미로 사용되었다는 것을 알 수 있다.

반응형