타입스크립트 - 제네릭

dobyming·2022년 12월 22일
0

Typescript Study

목록 보기
11/17
post-custom-banner

Generic이란 (제네릭)


  • 사용 목적 : 재사용성이 높은 컴포넌트를 만들때 활용합니다. 즉 여러가지 타입에서 동작하는 컴포넌트를 생성할때 유용합니다.

Generic을 왜 사용할까요?


🤔 사용 목적만 봤을때는 그래서 제네릭이 왜 필요한지 확 와닿지가 않습니다.
그래서 제네릭을 활용 안했을때의 단점에 대해서 설명코자 합니다.

1. 기존 타입정의 방식 vs 제네릭

함수 중복 선언으로 인한 유지보수성의 저하

동일한 기능을 하는 함수를 정의하고 싶지만, 파라미터의 type 한계점을 꼽을 수 있습니다.

즉 기존 타입정의 방식으로 정의한다면,
함수에 선언된 내용(기능)은 같지만 Param의 제한적 type으로 인해 파라미터의 타입만 다르고 기능은 동일한 불필요한 코드를 중복 작성하게 됩니다.

2. 유니온 타입을 이용한 선언 방식의 문제점

  1. type을 여러개 받을 수는 있지만, 교집합의 성질을 지니고 있기 때문에,
    한정된 method (string과 number 2개의 type을 모두 지원하는 메소드만) 를 지원받게 됩니다.
  2. 함수를 호출한 지역변수 역시 type의 대한 정의가 모호해짐

a 라는 변수에 hover 시, type으로 string | number 로 나옵니다. 그렇기 때문에 split() 이라는 string type을 지원하는 메소드를 인식하지 못하게 됩니다.

제네릭 usage


  • 구문 : function 제네릭명<T>(파라미터: T) : T {}
function logText<T>(text: T): T {
    console.log(text);
    return text;
}
// 1. <T> : 어떤 type을 받을건지 정의 (제네릭 선언)
// 2. 그 T를 인자(text)로 넘긴다.  
// 3. return할때도 해당 type으로 반환할 것을 선언

const str = logText<string>('abc');
str.split("");
const login = logText<boolean>(true);

logText 함수를 제네릭으로 선언하게 되면 <T> 에는 어떤 type이든지 들어올 수 있습니다.
이는 함수의 재사용성을 높여주고 유지보수성에도 효과적인 코드입니다.

따라서 Type을 string으로 부여했다면 str 변수의 type은 string으로 선언됩니다.
그리고 이에 따라 string type의 메소드를 지원합니다.

  • 인터페이스에서 제네릭을 선언하는 방법
interface DropdownItem<T> {
  value: T;
  selected: boolean;
}

const emails: DropdownItem<string>[] = [
  { value: 'naver.com', selected: true },
  { value: 'gmail.com', selected: false },
  { value: 'hanmail.net', selected: false },
];

const numberOfProducts: DropdownItem<number>[] = [
  { value: 1, selected: true },
  { value: 2, selected: false },
  { value: 3, selected: false },
];

인터페이스를 제네릭으로 선언하는 것 역시 반복적으로 선언되는 인터페이스 구문을 줄이기 위함이다.

emailsnumberOfProducts 모두 객체로 value와 selected를 값으로 전달받을때,
selected는 두 변수 모두 동일하게 boolean이지만
value에서 2가지로 분기된다. 이 부분을 활용하여 인터페이스를 제네릭화 할 수 있다.

따라서 위와 같이 value에 대해서 제네릭 T로 받게 되면 두 개의 변수에서 DropdownItem 인터페이스를 string 또는 number와 같이 유동적으로 사용할 수 있다.

제네릭의 타입 제한


  1. 정의된 타입 이용하기
interface lengthType {
    length: number;
}

function logTextLength<T extends lengthType>(text:T): T {
    text.length;
    return text;
}

logTextLength('a');
logTextLength({ length : 10 });

정의된 타입을 이용한다 = 인터페이스를 활용하겠다.

먼저 Type을 명세한 interface를 선언합니다.

그리고 logTextLength function에서 제네릭을 선언할 때, extends 를 통해 interface를 상속 받아 정의된 타입을 이용할 수 있습니다.

  1. keyof 로 타입 제한하기

keyof 는 인터페이스의 속성 중 하나를 택해서 사용하고 싶을 때 사용합니다.

interface shoppingItem {
    name : string;
    price : number;
    stock : number;
}

function getShoppingItemOption<T extends keyof shoppingItem>(itemOption : T): T {
    return itemOption;
}
getShoppingItemOption("name");

shoppingItem 인터페이스의 속성들 중에 타입을 선택해서 제네릭을 선언할 수 있습니다.

post-custom-banner

0개의 댓글