이안의 평일코딩

[TypeScript] 타입스크립트 기초 (5) - 제네릭 본문

Front-end/TypeScript

[TypeScript] 타입스크립트 기초 (5) - 제네릭

이안92 2021. 1. 21. 08:00
반응형

 

 제네릭이란?

 

제네릭(Generics)이란 함수를 정의할 때 타입을 비워놓은 상태에서 타입이 뭐가 들어갈 것인지 호출하는 시점에서 정의하는 것으로,

타입을 추론해서 최종 반환값까지 붙일 수 있다는 타입 정의에 대한 이점을 가지고 있다.

 

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

// <string> : 함수의 타입을 string으로 쓰겠다
// 파라미터와 반환값이 string이 될 것이라고 제네릭으로 선언
const str = logText<string>('abc')
제네릭을 쓰기 위한 빌드업 - 함수 중복 선언, 유니온 타입의 단점

아래와 같이 기존의 타입 정의 방식으로 각각 정의해서 함수를 중복으로 선언하면 문제없이 사용가능하지만

유지보수관점에서는 타입을 단순히 다르게 받기위해서 중복되는 코드를 계속해서 생산해 나가는 것은

가독성 그리고 전체적으로 코드가 많아지기 때문에 좋지 않다.

function logText(text: string) {
  console.log(text);
  text.split('').reverse().join('');
  return text;
}

function logNumber(num: number) {
  console.log(num);
  return num;
}

logText('a');
logNumber(10);

 

또한, 유니온 타입으로 선언한 반환값은 정확하게 한가지 타입으로 추론이 되어야만 API를 사용할 수 있기 때문에 좋지 않다.

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

const a = logText(30);
a.split('')
logText('a');

정확하게 타입을 알 수 있어야 split이 제공된다.

인터페이스에 제네릭 선언
interface Dropdown {
  value: string;
  selected: boolean;
}
const obj: Dropdown = {value:'abc', selected:false};

// 인터페이스에 제네릭을 선언하는 방법
interface Dropdown<T> {
  value: T;
  selected: boolean;
}
const obj: Dropdown<string> = {value: 'abc', selected:false};

하나의 인터페이스로 제네릭을 이용해 여러가지 타입을 커버해 타입코드를 줄여나갈 수 있는 장점이 있다.

interface DropdownItem<T> {
  value: T;
  selected: boolean;
}

// interface Email {
//   value: string;
//   selected: boolean;
// }

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

// interface ProductNumber {
//   value: number;
//   selected: boolean;
// }

const numberOfProducts: DropdownItem<number>[] = [
  { value: 1, selected: true },
  { value: 2, selected: false },
  { value: 3, selected: false },
];
제네릭의 타입 제한
function logTextLength<T>(text: T[]): T[] {
  console.log(text.length);
  text.forEach(function(text){ // 배열이라서 forEach도 제공됨
      console.log(text);
  })
  return text;
}
// 어떤 타입이 들어올지 알 수 없기 때문에 T에 [] 배열을 넘기면
// 타입 제한(힌트)를 줄 수 있다.

logTextLength<string>(['hi', 'abc']);

하위에 정의되어 있는 타입을 extends 해오면 구분할 수 있다.

// 정의된 타입으로 타입 제한
interface LengthType {
  length: number;
}

function logTextLength<T extends LengthType>(text: T): T{
  text.length;
  return text;
}
logTextLength('a'); // 문자열은 기본적으로 .length 속성 제공
logTextLength(10); // 숫자는 length가 제공되고 있지 않기 때문에 포함 불가
logTextLength({ length: 10 }); // 객체로 들어갈 수 있다

이미 정의되어 있는 타입에서 키값들만 들어갈 수 있게 keyof라는 예약어를 이용하여 제한한다.

(인터페이스에 있는 key들 중에 한 가지가 제네릭(타입)이 된다.)

// keyof로 타입 제한
interface ShoppingItem {
  name: string;
  price: number;
  stock: number;
}
function getShoppingItemOption<T extends keyof ShoppingItem>(itemOption: T): T {
  return itemOption;
}
getShoppingItemOption('name');
반응형
Comments