유틸리티 타입 Roll
form 관리 라이브러리 sicilian을 만들면서 여러 타입을 작성하다가 아래와 같은 타입을 만들었다. 여기서 사용되는 Input<K> 타입은 handleChange 함수의 인자로 들어오는 이벤트 객체의 타겟 이름이 특정한 K를 만족시키도록 강제한다. 아래의 스크린샷에서 확인할 수 있는 것처럼 이 타입의 추론은 두 개의 타입이 인터섹션되어있는 것으로 표시된다.
export type Input<K> = ChangeEvent<HTMLInputElement> & { target: { name: K; value: string } };

그러다 지인의 추천으로 네이버 사내 기술 교류 행사에서 발표된 타입스크립트 세션 영상을 보게되었다. 다양한 테크닉을 배울 수 있는 좋은 기회였지만, 그 중에서 내 눈을 사로잡은 건 유틸리티 타입 Role의 동작이었다. Role 타입의 제네릭에 객체의 인터섹션 타입을 제공하면, 마치 처음부터 인터섹션이 아니었던 것처럼 추론해준다.
type Roll<T> = { [K in keyof T]: T[K] } & {};

그런데 Role 타입의 제네릭에 원시 타입을 사용하면 그 타입 그대로 추론되고, 심지어는 리터럴 타입을 넣으면 리터럴 타입으로 추론된다. Roll<T>의 정의가 keyof T를 사용하는데, string은 그렇다 쳐도 number나 boolean은 어떻게 처리되길래 이런 식으로 추론되는건지 감도 잡히지 않는다.
type Test1 = Roll<string> // string type Test2 = Roll<number> // number type Test3 = Roll<"test"> // "test" type Test4 = Roll<1> // 1
아무튼 객체의 인터섹션 타입을 하나의 타입처럼 보이게 하는 것 뿐 아니라, 특정 타입을 복사해야할 때에도 Roll 타입이 유용하게 사용될 수 있을 것 같다.
블로그의 정보
Ayden's journal
Beard Weard Ayden