타입 챌린지 11 - Tuple to Object

소파의 벨로그·2025년 2월 21일

타입챌린지

목록 보기
3/131

문제 링크

문제

배열(튜플)을 받아, 각 원소의 값을 key/value로 갖는 오브젝트 타입을 반환하는 타입을 구현하세요.

내 풀이

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

튜플의 T[number]를 하면 유니온 타입으로 지정이 되고, 유니온 타입에 있는 값을 각각 키와 밸류로 갖는 mapped type이라고 정의했다.

다음과 같은 상황에서는 사용 측에서 에러가 나야하는 것도 문제에 정의되어 있었다

// @ts-expect-error
type error = TupleToObject<[[1, 2], {}]>

이러한 에러를 둔 이유는 mapped type의 키값에는 string|number|symbol만 들어갈 수 있기 때문이다.

그래서 이러한 사항을 고려함과 동시에, TupleToObject라는 타입명의 의미를 살리고자

들어올 수 있는 제네릭을 readonly (number|string|symbol)[]로 고정시켰다

관련 개념

해당 문제는 as const 개념과typeof, Mapped type 개념에 대한 문제였다

우선 as const suffix에 대한 개념을 알아볼 수 있다.
특정 변수 선언 시 뒷 부분에 as const를 붙이면 해당 변수에 할당된 값의 모든 프로퍼티들은 자동적으로 readonly가 되며,
numberstring같은 범용적인 타입이 아닌 리터럴 타입으로 선언되게 된다

// req.url의 타입은 string(x),"https://example.com"(o)
// req.method의 타입은 string(x),"get"(o)
const req = { url: "https://example.com", method: "GET" } as const;

그리고 typeof 개념도 확인할 수 있다.

typeof는 일반 js의 연산자로도 활용할 수 있지만, 타입 선언 시에도 들어갈 수 있다.

타입 선언시에 들어간다는 것은 무엇을 의미할까?

예를 들어
a라는 변수에 들어가는 값을 1이라는 리터럴 타입으로 지정할 수 있다.

let a:1=1;

// 아래 코드는 에러 x
a=1;
// 아래 코드는 에러 o(1이라는 리터럴 타입에 2는 들어갈 수 없기 때문)
a=2;

이걸 확인하면
다음 코드에서 js와는 다른 부분(즉 타입을 지정해주는 부분)이 보일 것이다

let value:number=1;

:number는 해당 변수에 들어갈 타입을 지정해주는 부분이고,
:number를 제외한 나머지 부분은 일반 js에서도 사용하는 것 처럼 변수를 선언하는 부분이다.

여기서 변수에 들어갈 타입을 지정해주는 부분에 typeof 변수명을 사용하면 그 변수에 할당된 타입을 그대로 사용할 수 있다

const a:'hello'|'world'='hello'

// 아래 코드는 let b:'hello'|'world'와 같음
let b:typeof a

// 아래 코드는 type TypaA='hello'|'world'와 같음
type TypeA=typeof a

js의 typeof연산자와 헷갈릴 수 있음에 주의하자.
js의 typeof연산자는 값이 할당되는 곳에 나오는 일종의 함수라고 생각하면 편하다

const a=1;

// typeof 1을 typeof(a)라고 생각하면 편하다(*실제 동작은 아님*)
// 아래 코드는 const b='nubmer'와 같다는 것을 알 수 있다
const b=typeof a

다른사람의 풀이

대부분의 풀이가 나의 풀이와 비슷했다

참고자료

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

0개의 댓글