본문 바로가기
TypeScript

[TypeScript] 타입스크립트 제네릭

by 깅민 2025. 5. 27.

1. 제네릭

함수의 매개변수, 리턴값의 타입을 함수를 호출하는 시점에 정하는 기능입니다.

 

제네릭은 타입 매개변수를 사용합니다.

T 가 아니라 다른 글자로 해도 가능합니다.

function echo<T>(message: T): T {
  console.log("msg", message);
  return message;
}

type Phone = {
  name: string;
  price: number;
  brand: string;
};

const phone = {
  name: "a",
  price: 1,
  brand: "b",
};

echo(1);
echo<string>("s"); // string 타입으로 지정
echo<object>(phone); // object 타입으로 지정

 

관례적으로 이렇게 사용합니다.

문자 설명
T 변수 타입에 사용 <T>
E 리스트 내부 요소에 사용 List<E>
K 키에 사용 Map<K, V>
V 값에 사용 Map<K, V>

 

1-1. 제약

광범위하게 사용할 수 있다보니 any 처럼 보이기도 합니다.

그래서 제약을 설정할 수 있습니다.

interface LengthCheck {
  length: number;
}

function check<T extends LengthCheck>(message: T) {
  console.log(message);
}

check([1, 2]);
check({ length: 10 });
check("test");
check(10); // 에러

 

LengthCheck 인터페이스를 상속받아 length 속성을 강제했습니다.

Number 타입에는 length 속성이 없어서 에러가 납니다.

 

이렇게도 쓸 수 있습니다.

function check<T extends { length: number }>(message: T) {
  console.log(message);
}

 

2. 함수 만들기

2-1. map

제네릭을 활용해요 map 함수를 만들어 봅니다.

 

function fn(arr: unknown[], callback: (item: unknown) => unknown):unknown[] {
  return [];
};

 

위 함수를 제네릭을 활용하면 아래처럼 쓸 수 있습니다.

배열을 받고 callback에 따라 새로운 결과 배열을 만듭니다.

function fn<T>(arr: T[], callback: (item: T) => T): T[] {
  let result = [];
  for (let i = 0; i < arr.length; i++) {
    result.push(callback(arr[i]));
  }
  return result;
}
const res = fn<number>([1, 2, 3], (a) => a * 2);
console.log(res); // [2, 4, 6]

 

그런데 만약 결과 배열을 string 배열이 되게 하고 싶다면 타입 매개변수를 추가해 해결할 수 있습니다.

U 라는 변수를 추가합니다.

function fn<T, U>(arr: T[], callback: (item: T) => U): U[] {
  let result = [];
  for (let i = 0; i < arr.length; i++) {
    result.push(callback(arr[i]));
  }
  return result;
}

const res = fn<number, string>([1, 2, 3], (a) => a.toString());
console.log(res); // ["2", "4", "6"]

 

3. 제네릭 인터페이스

제네릭은 인터페이스에 사용할 수 있습니다.

인터페이스에 사용할 때는 타입을 명시해주어야 합니다.

interface Obj<K, V> {
  key: K,
  value: V
}

let obj1: Obj<string, number> = {
  key: "a",
  value: 1
}

 

인덱스 시그니처를 활용하여

key 가 string이고 value도 string인 인터페이스를 만들었습니다.

interface Obj<V> {
  [key: string]: V;
}

let obj: Obj<string> = {
  key: "key",
  // key: 1, 에러
};

console.log(obj);