TypeScript에서 제네릭은 다양한 타입에 대해 동작하는 함수, 클래스, 인터페이스를 생성하는 데 사용되는 기능입니다.
제네릭을 사용하면 코드의 중복을 줄이고, 타입의 일관성을 유지하면서 재사용 가능한 구성 요소를 만들 수 있습니다.
제네릭은 타입이 정해지지 않은 상태에서 작성된 코드로, 실제로 사용할 때 전달되는 타입에 따라 동작합니다.
제네릭을 사용하면 유연한 타입 지정과 동시에 타입 안전성을 보장할 수 있습니다.
일반적으로 제네릭을 사용하는 함수, 클래스, 인터페이스는 대문자 T를 사용하여 타입을 표현합니다.
function identity<T>(arg: T): T {
return arg;
}
제네릭 함수를 사용할 때 타입을 지정하는 두 가지 방법이 있습니다.
let output = identity<string>("myString");
let output = identity("myString");
제네릭 인터페이스는 다양한 타입의 객체를 사용할 수 있는 인터페이스를 정의할 때 사용합니다.
인터페이스에서도 제네릭 타입을 사용할 수 있으며, 일반적으로 인터페이스 이름 뒤에 <T>
와 같은 형태로 표현합니다.
interface GenericIdentityFn<T> {
(arg: T): T;
}
function identity<T>(arg: T): T {
return arg;
}
let myIdentity: GenericIdentityFn<number> = identity;
제네릭 클래스를 사용하면 다양한 타입의 객체를 생성할 수 있는 클래스를 정의할 수 있습니다.
제네릭 클래스는 클래스 이름 뒤에 <T>
와 같은 형태로 표현하며, 클래스 내에서 사용되는 타입은 T로 표현합니다.
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };
제네릭 타입에 제약 조건을 추가하여 특정 속성이나 메서드가 포함된 타입만 사용하도록 할 수 있습니다.
이를 위해 extends
키워드를 사용하여 타입을 제한합니다.
예를 들어, 함수가 처리할 객체의 타입을 제한하고 싶은 경우, 인터페이스를 사용하여 제약 조건을 정의할 수 있습니다.
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
위 코드에서 T 타입은 Lengthwise 인터페이스를 상속받아야 하며, 따라서 length 속성을 포함해야 합니다.
이렇게 하면 loggingIdentity 함수를 사용할 때 전달되는 인자가 반드시 length 속성을 포함하도록 강제할 수 있습니다.
loggingIdentity({length: 10, value: 3}); // 가능
loggingIdentity(3); // 에러: '3'은 'Lengthwise' 타입에 할당할 수 없음
제네릭을 사용할 때 여러 타입 매개변수를 동시에 사용할 수도 있습니다.
이렇게 하면 더 다양한 타입의 조합을 할 수 있습니다.
function copyFields<T extends U, U>(target: T, source: U): T {
for (let id in source) {
if (source.hasOwnProperty(id)) {
target[id] = source[id];
}
}
return target;
}
let x = { a: 1, b: 2, c: 3, d: 4 };
let y = { b: 10, d: 20 };
let z = copyFields(x, y); // x와 y의 공통 필드를 복사합니다.