제네릭은 타입을 일반화하여 재사용성을 높이는 프로그래밍 기법입니다.
이를 통해 함수, 클래스 등을 다양한 타입에 대해 범용적으로 사용할 수 있게 됩니다.
제네릭 (Generics)
제네릭은 타입을 선언 시점이 아닌 생성 시점에 결정하는 프로그래밍 기법입니다.
이를 통해 함수, 클래스, 인터페이스 등을 다양한 타입에 대해 재사용할 수 있습니다.
제네릭의 기본 구조와 호출 방식은 다음과 같습니다.
T는 Type의 약자로써 주로 관용적으로 사용하는 타입 변수입니다.
function getValue<T>(value: T): T {
return value;
}
getValue<string>('hello');
getValue<number>(10);
getValue<boolean>(true);
둘 이상의 타입 변수는 주로 T 다음의 알파벳 순서대로 표현합니다.
function getValues<T, U>(a: T, b: U): [T, U] {
return [a, b];
}
등장 배경
단순히 매개변수를 그대로 반환하는 함수를 예로 들어보겠습니다.
이 함수는 string 타입만 인자로 받을 수 있기 때문에 범용성이 떨어집니다.
function getStringValue(value: string): string {
return value;
}
그래서 다른 타입들을 받고 싶다면 함수를 추가로 선언해야 하는 불편함이 있습니다.
function getNumberValue(value: number): number {
return value;
}
function getBooleanValue(value: boolean): boolean {
return value;
}
그렇다면 any 타입을 쓰면 어떨까요?
function getAnyValue(value: any): any {
return value;
}
any 타입은 어떤 타입이든 인자로 받을 수 있다는 점에서 제네릭과 비슷합니다.
그러나 함수의 매개변수와 리턴 값에 대한 타입 정보를 알 수 없습니다.
또한 any 타입은 타입 검사를 하지 않아 에러가 발생할 위험이 있습니다.
이러한 문제를 해결하기 위해 제네릭이 등장했습니다.
제네릭 인터페이스
제네릭을 인터페이스에 사용될 수 있습니다.
interface Foo<T, U> {
x: T;
y: U;
}
제네릭을 사용한 함수 인터페이스는 2가지 방식으로 표현 가능합니다.
interface Bar<T> {
(text: T): T;
}
interface Bar {
<T>(text: T): T;
}
제네릭 클래스
제네릭을 클래스에 사용하여 클래스 내부가 일관적인 타입을 가지도록 할 수 있습니다.
class Stack<T> {
private data: T[] = [];
constructor() {}
push(item: T): void {
this.data.push(item);
}
pop(): T {
return this.data.pop();
}
}
제네릭 화살표 함수
리액트에서 제네릭을 화살표 함수로 구현하면 에러가 발생합니다.
tsx 파일에서는 <T>
를 태그로 인식하기 때문입니다.
이를 해결하려면 extends를 사용하여 제네릭임을 알려야 합니다.
// Error
const func = <T>(arg: T): T => {
return arg;
}
// Ok
const func = <T extends {}>(arg: T): T => {
return arg;
};
'프론트엔드 > TypeScript' 카테고리의 다른 글
[TypeScript] 유틸리티 타입 구현하기 (Utility Types) (0) | 2022.06.07 |
---|---|
[TypeScript] Interface와 Type Alias의 차이점 (0) | 2022.06.06 |
[TypeScript] 타입 가드 (Type Guard) (0) | 2022.06.02 |
[TypeScript] 타입 단언 (Type Assertion) (0) | 2022.06.02 |
[TypeScript] 타입 종류 (0) | 2022.05.31 |