개발 알다가도 모르겠네요

타입스크립트의 제네릭 본문

웹/Typescript

타입스크립트의 제네릭

이재빵 2022. 6. 18. 07:33
728x90

제네릭은 어떠한 클래스 혹은 함수에서 사용할 타입을 그 함수나 클래스를 사용할 때 결정하는 프로그래밍 기법을 말합니다.

JavaScript는 원래 타입 선언이 필요하지 않고, 코드를 실행시키기 전까지는 함수와 클래스가 모든 타입에 대응합니다.

반면에 TypeScript는 정적 타입 언어이기 때문에, 기본적으로 타입을 정의한 함수 혹은 클래스는 모두 다른 타입에 재사용할 수 없습니다. 

 

제네릭을 사용하는 이유

다음과 같이 스택 자료구조를 TypeScript로 구현한다고 가정하면

class Stack {
  private data: any[] = [];

  contructor() {}

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

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

스택 같은 자료구조는 대개 범용적인 타입을 수용할 수 있도록 만들어집니다.

따라서 TypeScript에서는 위와 같이 any를 이용하여 구현하는 것이 가장 쉬운 방법입니다.

하지만 any를 이용하여 구현하면 저장하고 있는 자료의 타입이 모두 같지 않다는 문제점이 생기고,

위의 스택에서 자료를 추출하는 경우 런타임에서 항상 타입 검사를 해줘야 합니다.

const stack = new Stack();
stack.push(1);
stack.push('a');
stack.pop().substring(0) // 'a'
stack.pop().substring(0) // Throw TypeError

 

제네릭의 문법

클래스

스택 자료구조를 제네릭으로 다시 구현하면 다음과 같은 형태를 띄게 됩니다.

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

  constructor() {}

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

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

클래스 식별자 선언부에 <T>라는 못보던 문법이 추가됐습니다.

이 부분에는 식별자로 사용할 수 있는 것이라면 무엇이든 들어갈 수 있지만 대개의 경우 T를 사용합니다.

여기에서 T를 타입 변수(Type variables)라고 합니다.

이렇게해서 클래스에서 제네릭을 사용하겠다고 선언한 경우 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];
}

 

두 개 이상의 타입 변수

제네릭 함수나 클래스에서는 두 개 이상의 타입 변수도 사용할 수 있습니다.

다음과 같이 두 가지 변수를 받아 쌍으로 만들어 반환하는 함수에서 입력되는 두 가지의 변수의 타입이 다르다고 가정할 경우 두 가지의 타입 변수가 필요하게 됩니다.

function toPair<T, U>(a: T, b: U): [ T, U ] {
  return [ a, b ];
}
toPair<string, number>('1', 1); // [ '1', 1 ]
toPair<number, number>(1, 1); // [ 1, 1 ]

 

상속된 타입 변수

타입 변수는 기존에 사용하고 있는 타입을 상속할 수도 있습니다.

function getFirst<T extends Stack<U>, U>(container: T): U {
  const item = container.pop();
  container.push(item);
  return item;
}

getFirst는 입력받은 스택의 첫번째 요소를 반환하는 함수입니다.

만약 첫 번째 타입으로 스택, 혹은 스택을 상속한 타입이 아닌 다른 타입을 사용하면 에러가 발생합니다.

getFirst<Stack<number>, number)(numberStack);
getFirst<number, number>(1); // Type 'number' does not satisfy the constraint 'Stack<number>'.

' > Typescript' 카테고리의 다른 글

타입스크립트의 Enum  (0) 2022.06.19
타입스크립트의 함수  (0) 2022.06.18
타입스크립트의 인터페이스  (0) 2022.06.18
타입스크립트의 Class  (0) 2022.06.17
타입스크립트란 무엇인가?  (0) 2022.06.17