Typescript 를 써서 함수를 작성하려고 보면 제네릭 (Generic) 을 쓸 일이 종종 있다. 함수에는 인자를 넣어 전달할 수 있듯이, 일종의 '타입 인자' 같은 것인데, 보통은 T
와 같은 컨벤션으로 많이들 사용한다.
그런데 오늘 개발을 하다가 난관에 봉착했는데 바로 다음과 같은 상황이었다.
다음의 함수를 보자
export function selectDataMaker<T>(lookupTable: Record<T, string>) {
return Object.entries(lookupTable).map(([value, label]) => ({ value, label }));
}
우선 Record
가 나오는데, 이는 특정 타입에 대한 어떤 lookup table 과 비슷한 것을 만들고자 할 때 유용하게 쓰인다. 자세한 내용은 이전 포스팅 여기 참고!
즉, 여기서 입력값인 lookupTable
은 어떤 T
라는 타입이 가질 수 있는 항목들을 key 로 갖고, 그 각각에 해당하는 value 로 string 형식이 오는 것이다.
예를 들어,
export type UserType = "admin" | "seller" | "customer"
export const USER_TYPE_LOOKUP_TABLE: Record<UserType, string> = {
"admin" : "관리자",
"seller" : "판매자",
"customer" : "고객",
}
이런 식이다.
하지만 저런 상태의 함수로는 아래와 같은 error 가 뜬다.
즉, T
의 타입이 Record
의 첫 제네릭 인자로 오지 못하는 것이 올 수도 있다는 것이다. 왜냐하면 Record
의 첫 인자로는 string
, number
, symbol
밖에 오지 못하기 때문이다.
즉, 우리는 제네릭 <T>
에 이러한 제약을 걸어줘야 하는데, 바로 이 때 쓰는 것이 extends
이다.
아래와 같이 고쳐보자.
export function selectDataMaker<T extends string | number | symbol>(
lookupTable: Record<T, string>
) {
return Object.entries(lookupTable).map(([value, label]) => ({ value, label }));
}
여기서 이제 <T>
는 string
, number
, symbol
로 제한된다.
그리하여 오류가 없어졌다. extends
를 이렇게 타입을 제한할 때도 활용할 수 있다!