제네릭

·2023년 11월 20일

제네릭이란?

타입을 미리 정의하지 않고, 상요하는 시점에 원하는 타입을 정의해서 쓸 수 있는 문법

제네릭 기본 문법

function getText<T>(text: T) :T{
  return text;
}

이와 같이 작성하면, getText() 함수를 실행할 때 아무 타입이나 넘길 수 있다.
따라서, 아래와 같이 함수를 불러오면,

getText<string>('hi');

function getText<string>(text:string):string{
  return text;
}

위와 같이 정의된 함수로 생각할 수 있고,

function getText(text: string):string{
  return text;
}

와 같이 선언된 것과 같은 효과를 나타낸다.

제네릭 사용 이유

중복되는 타입 코드 감소

반복되는 타입 코드를 줄여줄 수 있다. 만약, 제네릭이 없다면 위와 같은 함수는

function getText(text: string):string{
  return text;
}

function getNumber(num: number):number{
  return num;
}

다음과 같이 계속해서 로직은 같지만 타입만 다른 함수를 작성해야 한다.
즉, 타입 때문에 같은 로직을 가진 코드를 중복해서 선언하게 된다.
이와 같은 문제는 추가적인 타입을 처리해야 할 때 더욱 큰 문제가 될 수 있다.

따라서, 이와 같은 문제를 해결하기 위해서 any를 쓴다면, 또 다른 문제가 발생한다.
타입 스크립트의 에러 사전 방지 기능이 사라지고, 타입스크립트를 쓰는 이유가 사라지기 때문이며 자동 완성 또한 불가능해진다.

인터페이스와 제네릭

인터페이스를 선언할 떄, 모든 데이터의 타입을 일일이 선언하게 되는 경우가 존재한다.
이때 제네릭을 사용할 수 있다.
예시는 다음과 같다.

interface MySpecials {
  value: { dolls: string; piano: string; }
  isPerson: boolean;
}

이를 제네릭을 ㅗ바꾸면,

interface MySpecials<T> {
  value: T;
  isPerson: boolean;
}

따라서 각각을 연결할 때에는

let doll : MySpecials<string>;
let phone: MySpecial<string>;
let them: MySpecial<{doll:string; piano:number}>

제네릭의 타입 제약

extends를 이용한 타입 제약

제네릭으로 특정 타입만 받을 수 있도록 제약을 걸 수 있으며 extends 를 이용한다.

function myFavoritePalce<T extends string>(place: T):T {
  return place
}

이와 같은 부분에서 함수를 호출할 때 제네릭에 string을 넘길 수 있지만,
다른 타입을 넘기게 되면 에러가 발생한다.

내장된 속성이 있을 때에만 타입을 취급하는 경우에 타입 제약을 쓸 수 있다.
예를 들어,

function myFavoritePalce<T extends { length: number} >(place: T):T {
  return place
}

위의 커드에서, 함수의 인자로 넘길 수 있는 데이터 타입은 length 속성을 가지고 있는 문자열, 배열, length 속성을 갖는 객체로 세 가지로 제약이 이루어 진다.

keyof를 이용한 타입 제약

keyof는 특정 타입의 키 값을 추출해서 문자열 유니언 타입으로 변환시켜 준다.
예시로 살펴보자

type FavoritePlaces = keyof { name: string; place :string; }

따라서 이때 FavoritePlaces 는 객체의 키가 유니언 타입 ("name" | "place") 로 변환된다. 따라서 이를 제네릭과 연결하면, 다음과 같다.

function myFavorites<T extends keyof {name: string; place: string;}>(value: T) {
  console.log(value)
}

따라서 함수를 호추할 때 넘길 수 있는 문자여릉ㄹ 'name'과 'place'이다.
다른 값을 넘기면, 에러가 발생한다.

제네릭 사용시 주의할 사항

다음과 같은 코드는 에러가 발생할 수 있다.

function myFavoritePlace<T>(place: T){
  console.log(place.length);
}
myFavoritePlace<string>('myhome');

위와 같은 코드는 제네릭으로 선언되고 제네릭 값을 string으로 받아주었기에,
에러가 발생하지 않을 것이라 예상할 것이다.
하지만,
타입스크립트 컴파일러는 함수에 어떤 타입이 들어올 지 모르기 때문에 함부로 타입을 가정하지 않는다.
따라서, 자동완성이 되지 않고 동시에 각 타입마다 특정한 method 등을 함수 안에 넣으면, 에러가 발생할 수 있다.

이와 같은 코드는 타입 제약을 통해 해결할 수 있다.

function myFavoritePlace<T extends { length:number} >(place:T){
  console.log(place.length);
}
myFavoritePlace<string>('myhome'); //6
profile
new blog: https://hae0-02ni.tistory.com/

2개의 댓글

comment-user-thumbnail
2023년 11월 23일

어려운 개념처럼 느껴졌는데 너무 깔끔하게 정리해주서 다시 복습해볼 수 있었어요! 진짜 제네릭을 하용하면 불필요하게 중복되는 타입코드들을 줄일 수 있겠네요!! 특히나 length 속성이 있는 제네릭 타입만 넘길 수 있게 되는 부분을 예시랑 함께 적어줘서 이해가 잘 되었습니다:) 유익한 글 너무 잘 읽었어요 혠니 최고👍🥹

답글 달기
comment-user-thumbnail
2023년 11월 24일

깔끔한 정리 덕분에 이해가 잘 되었네요.
주의사항이 함께 적혀있는부분이 가장 유용했던 거 같습니다 감사합니다 !

답글 달기