[TypeScript] 제네릭

정호·2023년 6월 1일
0

TIL

목록 보기
4/19

제네릭(Generics)이란?

제네릭은 C#, Java 등의 언어에서 재사용성이 높은 컴포넌트를 만들 때 자주 활용되는 특징입니다. 특히, 한가지 타입보다 여러 가지 타입에서 동작하는 컴포넌트를 생성하는데 사용됩니다.

제네릭이란 타입을 마치 함수의 파라미터처럼 사용하는 것을 의미

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

위 함수는 제네릭 기본 문법이 적용된 형태입니다. 이제 함수를 호출할 때 아래와 같이 함수 안에서 사용할 타입을 넘겨줄 수 있다.

getText<string>('hi');
getText<number>(10);
getText<boolean>(true);

제네릭은 왜 사용할까?

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

이 함수의 인자와 반환 값은 모두 string으로 지정되어 있지만 만약 여러 가지 타입을 허용하고 싶다면 아래와 같이 any를 사용할 수 있습니다

function logText(text: any): any {
  return text;
}

이렇게 타입을 바꾼다고 해서 함수의 동작에 문제가 생기진 않습니다. 다만, 함수의 인자로 어떤 타입이 들어갔고 어떤 값이 반환되는지는 알 수가 없습니다. 왜냐하면 any라는 타입은 타입 검사를 하지 않기 때문입니다.

이러한 문제점을 해결할 수 있는 것이 제네릭입니다. 제네릭은 다음과 같이 사용합니다.

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

먼저 함수의 이름 바로 뒤에 라는 코드를 추가했습니다. 그리고 함수의 인자와 반환 값에 모두 T 라는 타입을 추가합니다. 이렇게 되면 함수를 호출할 때 넘긴 타입에 대해 타입스크립트가 추정할 수 있게 됩니다. 따라서, 함수의 입력 값에 대한 타입과 출력 값에 대한 타입이 동일한지 검증할 수 있게 됩니다.

그리고 이렇게 선언한 함수는 아래와 같이 2가지 방법으로 호출할 수 있다.

// #1
const text = logText<string>("Hello Generic");
// #2
const text = logText("Hello Generic");

제네릭의 문법

클래스

class Stack<T> {
  private data: T[] = [];

  constructor() {}

  push(item: T): void {
    this.data.push(item);
  }

  pop(): T {
    return this.data.pop();
  }
}

생성자를 호출하여 객체를 만들 때 T로 사용될 타입을 지정해주기만 하면 된다.

const numberStack = new Stack<number>();
const stringStack = new Stack<string>();
numberStack.push(1);
stringStack.push('a');  

이제 각 스택은 항상 생성할 때 선언한 타입만을 저장하고 리턴한다. 이렇게 하면 컴파일러가 리턴하는 타입을 알 수 있게 되므로 에디터에서 자동 완성을 사용할 수 있게 되므로 생산성 향상에도 기여한다는 장점이 있다.

함수

function first<T>(arr: T[]): T {
  return arr[0];
}  

함수를 호출할 때 제네릭 문법으로 타입을 정해주기만 하면 된다.

first<number>([1, 2, 3]); // 1  

두 개 이상의 타입 변수

제네릭 함수나 클래스에서는 두 개 이상의 타입 변수도 사용할 수 있다. 다음과 같이 두 가지 변수를 받아 쌍으로 만들어 반환하는 함수를 구현해야 한다고 가정하자.

function toPair<T, U>(a: T, b: U): [ T, U ] {
  return [ a, b ];
} 

제네릭을 사용하면 위와 같은 형태로 구현할 수 있다. 꺽쇠 안에 T와 U 두 가지의 타입 변수가 보일 것이다. 아까 관용적으로 T를 사용한다고 말했는데, 그 뒤로는 알파벳 순서대로 사용하면 된다. 반복문에서 관용적으로 인덱스 변수로 i, j를 사용하는 것과 비슷하다.

toPair<string, number>('1', 1); // [ '1', 1 ]
toPair<number, number>(1, 1); // [ 1, 1 ]  

참고 https://joshua1988.github.io/ts/guide/generics.html#%EC%A0%9C%EB%84%A4%EB%A6%AD-generics-%EC%9D%98-%EC%82%AC%EC%A0%84%EC%A0%81-%EC%A0%95%EC%9D%98

profile
열심히 기록할 예정🙃

0개의 댓글