TS_제네릭

sunjin·2024년 1월 2일

Typescript

목록 보기
5/5
post-thumbnail

두루두루 모든 타입의 값을 다 적용할 수 있는 범용적인 함수

function func<T>(value: T): T {
  return value;
}

let num = func(10);
// number 타입

제네릭동작원리

  • 타입 할당도 가능하다
function func<T>(value: T): T {
  return value;
}

let arr = func<[number, number, number]>([1, 2, 3]);

🩵 여러가지 사례

1️⃣ 2개의 타입 변수가 필요한 상황

function swap<T, U>(a: T, b: U) {
  return [b, a];
}

const [a, b] = swap("1", 2);

2️⃣ 배열 타입을 인수로 받는 제네릭 함수

function returnFirstValue<T>(data: T[]) {
  return data[0];
}

let num = returnFirstValue([0, 1, 2]);
// number

let str = returnFirstValue([1, "hello", "mynameis"]);
// number | string

3️⃣ 반환값의 타입이 배열의 첫번째 요소 타입이 되도록

function returnFirstValue<T>(data: [T, ...unknown[]]) {
  return data[0];
}

let str = returnFirstValue([1, "hello", "mynameis"]);
// number

4️⃣ length 프로퍼티를 갖는 객체 타입 제한

function getLength<T extends { length: number }>(data: T) {
  return data.length;
}

getLength("123");            // ✅

getLength([1, 2, 3]);        // ✅

getLength({ length: 1 });    // ✅

getLength(undefined);        // ❌

getLength(null);             // ❌

🩵 map, foreach 메서드 타입 정의

✨ map 메서드 타입 정의

const arr = [1, 2, 3];
const newArr = arr.map((it) => it * 2);
// [2, 4, 6]


function map<T, U>(arr: T[], callback: (item: T) => U): U[] {
  (...)
}

map(arr, (it) => it.toString());
// string[] 타입의 배열을 반환
// 결과 : ["1", "2", "3"]

map(arr, (it) => it * 2);
map(["hi", "hello"], (it) => parseInt(it));
  • 원본 배열의 각 요소에 콜백함수를 수행하고 반환된 값들을 모아 새로운 배열로 만들어 반환한다.

✨ foreach 메서드 타입 정의

const arr2 = [1, 2, 3];

arr2.forEach((it) => console.log(it));
// 출력 : 1, 2, 3

function forEach<T>(arr: T[], callback: (item: T) => void) {
  for (let i = 0; i < arr.length; i++) {
    callback(arr[i]);
  }
}
  • foreach는 배열의 모든 요소에 콜백함수를 한번씩 수행해주는 메서드
  • 2개의 매개변수를 받고, foreach 메서드는 반환값이 없는 메서드 이므로 콜백함수의 반환값 타입을 void로 정의한다.

🩵 제네릭 인터페이스

interface KeyPair<K, V> {
  key: K;
  value: V;
}

let keyPair: KeyPair<string, number> = {
  key: "key",
  value: 0,
};

let keyPair2: KeyPair<boolean, string[]> = {
  key: true,
  value: ["1"],
};
  • 제네릭 인터페이스는 제네릭 함수와는 달리 변수의 타입으로 정의할 때 반드시 꺽쇠와 함께 타입 변수에 할당할 타입을 명시해주어야 한다.
    이유는 제네릭 함수는 매개변수에 제공되는 값의 타입을 기준으로 타입 변수의 타입을 추론할 수 있지만 인터페이스는 마땅히 추론할 수 있는 값이 없기 때문이다.

✨ 인덱스 시그니쳐와 함께 사용하기


  • 인덱스 시그니쳐가 뭐였지

객체 타입을 유연하게 정의할 수 있도록 돕는 특수한 문법

type CountryCodes = {
  [key: string]: string;
};

let countryCodes: CountryCodes = {
  Korea: "ko",
  UnitedState: "us",
  UnitedKingdom: "uk",
  // (... 약 100개의 국가)
  Brazil : 'bz'
};
  • [key: string] : string 은 인덱스 시그니쳐 문법이다. 이 객체 타입에는 key가 string 타입이고 value가 string 타입인 모든 프로퍼티를 포함된다.

interface NumberMap {
  [key: string]: number;
}

let numberMap1: NumberMap = {
  key: -1231,
  key2: 123123,
};

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

let StringMap: Map<string> = {
  key: "value",
};

let booleanMap: Map<boolean> = {
  key: true,
};

✨ 제너릭 타입 별칭

인터페이스와 마찬가지로 타입 별칭에도 제너릭을 적용할 수 있다.

type Map2<V> = {
  [key: string]: V;
};

let stringMap2: Map2<string> = {
  key: "string",
};
profile
🍀

0개의 댓글