[TypeScript] Generic

김재훈·2023년 7월 19일

제네릭(generic)은 하나의 데이터 타입이 아닌 여러 데이터 타입에 대해 클래스/인터페이스 혹은 함수가 동일하게 동작할 수 있게 해주는 기능이다. 쉽게 말해, 타입을 변수화 한 것이다.

제네릭에는 함수에 제네릭 타입을 적용한 제네릭 함수, 인터페이스에 제네릭 타입을 넣은 제네릭 인터페이스, 클래스에 제네릭을 적용한 제네릭 클래스 등이 있다.

제네릭을 사용하면 재사용성이 높은 함수와 클래스를 생성할 수 있다. 그리고 오류를 쉽게 포착할 수 있다.

제네릭 함수

// number 타입의 매개변수를 return하는 함수
function NumberReturnFunc(arg: number): number {
  return arg;
}

// string 타입의 매개변수를 return하는 함수
function StringReturnFunc(arg: string): string {
  return arg;
}

// boolean 타입의 매개변수를 return하는 함수
function BooleanReturnFunc(arg: boolean): boolean {
  return arg;
}

위는 매개변수를 그대로 반환하는 함수들인데 함수의 기능은 똑같지만, 매개변수의 타입과 반환하는 타입이 다르다는 이유로 여러 개의 함수를 구현했다. 이럴 때 제네릭 함수를 사용하여 한 개의 함수로 구현할 수 있다.

function GenericReturnFunc<T>(arg: T): T {
  return arg;
}

제네릭 함수 구현 방법은 함수명 뒤에 <T>를 추가하며, T를 매개변수의 타입 또는 반환 타입으로 설정할 수 있다. 꼭 <T>로 작성할 필요는 없다.

위에서 만든 제네릭 함수는 다음과 같이 호출할 수 있다.

let numVar = GenericReturnFunc<number>(123);
let strVar = GenericReturnFunc<string>('ABC');

제네릭 화살표 함수

let GenericReturnFunc = <Type>(arg: Type): Type => {
  return arg;
}

제네릭 화살표 함수를 위와 같이 구현하는 경우 에러가 발생한다. .ts 확장자 파일에서는 정상적으로 작동할 수 있는데 .tsx 확장자 파일은 TypeScript + JSX로 구성되어 있어서 <Type>에서 태그(<>) 문제가 발생한다.

.tsx 확장자 파일에서 제네릭 화살표 함수를 구현해야 하는 경우 제네릭 매개변수에 extends를 사용하여 컴파일러에게 제네릭 화살표 함수라고 알려주어야 한다.

let GenericReturnFunc = <Type extends {}>(arg: Type): Type => {
  return arg;
}

제네릭 인터페이스

// 제네릭 인터페이스
interface Mobile<T> {
  name: string;
  price: number;
  option: T; // 제네릭 타입 - option 속성에는 다양한 타입의 데이터가 들어온다
}

// 제네릭 자체에 리터럴 객체 타입 할당
const m1: Mobile<{ color: string; coupon: boolean }> = {
  name: 's21',
  price: 1000,
  option: { color: 'black', coupon: false }, // 제네릭 타입에 의해 option 속성 할당
};

const m2: Mobile<string> = {
  name: 's20',
  price: 900,
  option: 'used' // 제네릭 타입에 의해 option 속성 할당
};

제네릭 클래스

class Queue<T> {
  protected data: Array<T> = [];
  
  push(item: T) {
    this.data.push(item);
  }
  
  pop(): T | undefined {
    return this.data.shift();
  }
}

const numberQueue = new Queue<number>();

numberQueue.push(0);
numberQueue.push(1);
numberQueue.push('1'); // error

제네릭 타입 제약

제네릭의 타입 파라미터는 기본적으로 모든 타입을 받아들일 수 있다. 만약 모든 타입을 받아들이지 말고, 제약된 타입들만 받아들이고자 한다면, 이러한 제약을 타입 파라미터에 지정할 수 있는데, 이를 제네릭 타입 제약이라고 한다.

const printMessage = <T extends string | number>(message: T): T => {
  return message;
}

printMessage<string>('abc'); // abc
printMessage<number>(123); // 123
printMessage<boolean>(false); // error : Type 'boolean' does not satisfy the constraint 'string | number'.

extends 키워드로 제약조건을 걸어준다. 만약 제약조건을 벗어나는 타입을 선언하면 에러가 발생한다.

참고

profile
김재훈

0개의 댓글