제네릭은 타입을 동적으로 정의할 수 있는 기능이라고 한다. 그렇기때문에 재사용성을 높일 수 있는 타입이라 할 수 있다.
function f1(a: number, size: number): number[] {
const arr: number[] = [];
for (let i = 0; i < size; i++) {
arr.push(a);
}
return arr;
}
function f2(a: string, size: number): string[] {
const arr: string[] = [];
for (let i = 0; i < size; i++) {
arr.push(a);
}
return arr;
}
위 예시와 같이 f1, f2를 만들어 number타입과 string타입을 구분지을 수 있었다. 아니면 function-overload를 통해서 통합하여 만들수도 있다.
function f1(a: number, size: number): number[];
function f1(a: string, size: number): string[];
function f1(a: number | string, size: number): Array<number | string> {
const arr: Array<number | string> = [];
for (let i = 0; i < size; i++) {
arr.push(a);
}
return arr;
}
이전 함수에 비하여 효율적이지만 타입을 추가할경우 f1을 수정해줘야하는 번거로움이 발생할 것이다. 이것을 줄여주는 방법으로 제네렉타입을 사용하는 것이라고 한다.
제네릭은 다음과 같이 사용한다고 한다.
function f1<T>(a: T, size: number): T[] {
const arr: T[] = [];
for (let i = 0; i < size; i++) {
arr.push(a);
}
return arr;
}
const a1 = f1<number>(1,5)
const a2 = f1<string>("a",10)
const a3 = f1("z", 10);
const a4 = f1(5, 10);
함수에서 제네렉은 함수 혹은 클래스 옆에<T>
와 같이 사용하며, "T"는 관용적인 식별자 일 뿐이다. T가 공통적으로 매개변수 "a"와 return 타입과 함수안에서 arr 타입을 정해주고 있다. 변수 a1, a2처럼 타입을 지정해주어도 되지만, a3, a4 처럼 타입스크립트에서 자동으로 지정해 줄 수 있다.
제네릭에서 extends 키워드를 사용하여 타입을 제한하거나 확장 할 수 있다고한다.
function f1<T extends number | string>(a: T): T {
return a;
}
console.log(f1(1));
console.log(f1("a"));
console.log(f1(true));
//error TS2345:
//Argument of type 'boolean' is not assignable to parameter of
//type 'string | number'.
위 예시와 같이 T라는 제네렉 타입에 number타입과 string타입으로 제한해 놓으면 그 밖에 타입인 불리언타입이 들어왔을 경우 에러가 뜨는것을 알 수 있었다.
interface Animal {
name: string;
age: number;
}
interface Cat extends Animal {
cutable: boolean;
}
function swapProperty<T extends Cat, K extends keyof Animal>(
p1: T,
p2: T,
key: K
): void {
const temp = p1[key];
p1[key] = p2[key];
p2[key] = temp;
}
const p1: Cat = {
name: "nyang",
age: 2,
cutable: true,
};
const p2: Cat = {
name: "nyangnyang",
age: 3,
cutable: true,
};
console.log(p1, p2);
//{ name: 'nyang', age: 2, cutable: true }
//{ name: 'nyangnyang', age: 3, cutable: true }
swapProperty(p1, p1, "name");
console.log(p1, p2);
//{ name: 'nyang', age: 2, cutable: true }
//{ name: 'nyangnyang', age: 3, cutable: true }
위 예시에서는 인터페이스 Animal 을 상속받은 인터페이스 Cat을 선언하였고, 제네릭을 활용하여 swapProperty라는 함수를 정의하였다. "T" 는 인터페이서 Cat을 제한한 것이고, K는 extents keyof라는 인터페이스 Animal에서 key값만 가져올 수 있게 제한하였다. 즉, "name"과 "age"를 의미하는 것이다.
[제네릭, TypeScript Guidebook, 2022년08월8일 접속]
https://yamoo9.gitbook.io/typescript/generics