- Published on
제네릭(Generic) 타입
Generic을 사용하는 이유
-
제네릭은 어떠한 클래스 혹은 함수에서 사용할 타입을 그 함수나 클래스를 사용할 때 결정하는 프로그래밍 기법을 말한다.
-
특정 타입을 위해 만들어진 클래스나 함수는 다른 타입을 위해 재사용할 수가 없다. 제네릭을 사용하면 다양한 타입을 처리하여 범용적으로 사용할 수 있다.
Generic을 사용한 Class 구현
- 아래는 타입 변수 'T' 를 사용하는 Stack Class 이다. 해당 클래스를 사용하는 시점에 타입 변수 'T'에 원하는 타입을 바인딩 할 수 있다.
class Stack<T> {
private data: T[] = [];
constructor() {}
push(item: T): void {
this.data.push(item);
}
pop(): T {
return <T>this.data.pop();
}
}
- 위에서 정의한 Stack Class를 사용하면서 type 을 지정해주고 있다. 지정한 type과 다른 타입을 push하면 컴파일 에러가 발생한다.
const numberStack = new Stack<number>();
const stringStack = new Stack<string>();
numberStack.push(1);
stringStack.push('a');
Generic 함수 구현
- 함수 호출시 타입을 지정해준다.
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];
}
toPair<string, number>('1', 1); // [ '1', 1 ]
toPair<number, number>(1, 1); // [ 1, 1 ]
Generic 제약조건 (Generic Constraints)
- 아래 예제에서 arg의 length 프로퍼에 접근하려고 하지만 컴파일러는 모든 타입이 length 속성을 가지고 있음을 알 수 없다.
function loggingIdentity<T>(arg: T): T {
console.log(arg.length); // 오류 : T는 .length 메소드를 가지고 있지 않습니다.
return arg;
}
-
위 예제에서 모든 타입으로 작업하는 대신 length 프로퍼티를 가진 모든 타입에서 동작하도록 제한을 걸어야 한다.
-
타입변수 상속을 통하여 제약조건을 걸어서 이 문제를 해결 할 수 있다.
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length); // 이제 .length 프로퍼티가 있으므로 더이상 오류가 없습니다.
return arg;
}
loggingIdentity({ length: 10, value: 3 }); // length가 필수 프로퍼티 이므로 같이 전달해야 한다.