[Typescript] 타입스크립트 제네릭

problem_hun·2023년 3월 16일
0

타입스크립트

목록 보기
11/16
post-thumbnail

제네릭

타입스크립트에서는 함수의 파라미터나 리턴값을 함수를 선언할 때 미리 지정해줬어야 했다.
이번엔 타입을 호출하는 순간 지정해 줄 수 있는 제네릭에 대해 배워보자.

제네릭이란 타입을 마치 함수의 파라미터처럼 받아서 사용하는 것을 의미한다.
타입을 비워놓은 상태에서 어떤 타입을 받을 것인지 호출할 때 정의하는 것이다.

function logText<T>(text: T): T {
  console.log(text);
  return text;
}
// 호출할 때 타입을 지정 가능
const str = logText<string>("abc");
const login = logText<boolean>(true);

 


제네릭 활용

여러 타입을 사용하고자 한다면 유니온 타입을 사용하면 될 텐데, 왜 제네릭을 사용하는 걸까?
함수의 파라미터를 유니온 타입으로 받을 시, 인풋에 대한 문제는 해결되지만 아웃풋에 대한 문제가 발생하게 된다.

function logText(text:string|number){
  console.log(text);
  return text
}

const hello = logText('hello world')
hello.split('');

위의 경우 변수 hello는 유니온 타입으로 string과 number를 받을 수 있다. 하지만 변수를 만들고 메서드를 사용할 때에는 변수가 둘 중 어떤 타입인지 인식하지 못해 string과 number타입에서 모두 가능한 메서드만 사용이 가능해진다.

따라서 string타입으로 변수를 선언하더라도 string타입의 메서드인 split()을 사용하지 못한다.

하지만 제네릭으로 string타입의 변수를 선언하면 온전히 string타입에 대한 메서드를 모두 사용할 수 있게 된다.

function logText<T>(text: T): T {
  console.log(text);
  return text;
}

const str = logText<string>("abc");
str.split(''); ⭕️

 

API함수의 리턴 타입 정의

제네릭이 많이 쓰이는 요소 중 하나로 API의 응답 데이터의 규격을 정의할 때 많이 쓰인다. Promise 자체가 제네릭을 받도록 되어있으므로 함수의 반환값의 타입으로는 Promise<제네릭> 으로 설정해주어야 한다.

function fetchItems():Promise<string[]> {
  let items = ['a', 'b', 'c'];
  return new Promise((resolve) => {
    resolve(items);
  });
}

쉽게 말해 API로 비동기적으로 데이터를 받게 된다면, 어떠한 형식으로 받을지 모르기 때문에 리턴 타입을 제네릭방법으로 지정해줘야 하는 것이다.

 

인터페이스에 제네릭 활용

다음과 같이 인터페이스 형식에도 제네릭을 사용할 수 있다.

interface Dropdown<T> {
  value: T;
  selected: boolean;
}
const obj1: Dropdown<number> = { value: 10, selected: true };
const obj2: Dropdown<string> = { value: "abc", selected: true };

제네릭의 타입 제한

파라미터로 제네릭을 받는 함수는 내부 코드에 에러를 일으킬 수 있다.
해당 함수가 호출되기 전까지는 어떤 타입이 지정될 지 모르기 때문이다.

function logTextLength<T>(text: T): T {
  console.log(text.length); //❌ 호출하기 전에는 어떤 타입인지 지정되지 않아서 에러!
  return text;
}

logTextLength("hello");

하지만 제네릭에 타입을 제한하면 제한된 타입의 메서드는 사용이 가능해진다.
아래의 코드는 파라미터의 타입을 배열로 제한한 예시이다.

function logTextLength<T>(text: T[]): T[] {
  //호출되기 전에 타입을 배열로 제한해서 내부 함수에 에러를 없앨 수 있다.
  console.log(text.length);
  return text;
}
logTextLength<string>(["hello"]);

정의된 타입으로 타입 제한

length 메서드가 있는 타입만 지정할 수 있도록 다음과 같이 제네릭의 타입을 제한할 수 있다.

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

toString 메서드가 있는 타입만 지정하도록 하려먼 다음과 같이 제네릭의 타입을 제한할 수 있다.

function createDropdownItem<T extends { toString: Function }>(item: DropdownItem<T>
) {
  const option = document.createElement("option");
  option.value = item.value.toString();
  option.innerText = item.value.toString();
  option.selected = item.selected;
  return option;
}

keyof로 타입 제한

정의되어 있는 인터페이스의 키값을 제네릭의 타입으로 제한하여 코드를 짤 수도 있다.
제네릭에 extends keyof <인터페이스 명> 의 형식으로 가능하다.

interface ShoppingItem {
  name: string;
  price: number;
  stock: number;
}
function getShoppingItemOption<T extends keyof ShoppingItem>(itemOption: T): T {
  return itemOption;
}
getShoppingItemOption("price"); //⭕️
getShoppingItemOption(price); //❌
getShoppingItemOption(15443); //❌

파라미터는 ShoppingItem의 키 값(name, price, stock) 중 한가지만 들어올 수 있다.

profile
문제아

0개의 댓글