TypeScript | 제네릭(Generics)

Kate Jung·2022년 3월 9일
0

TypeScript

목록 보기
9/10
post-thumbnail

(선언 시, 타입 파라미터만 적고) 생성 시점에 사용할 타입을 결정

📌 타입 파라미터

  • <T>

    • 기입해주지 않아도 TS는 전달되는 매개변수로 타입 파악함.

    • T

      • 보통 T 사용 (다른 문자 무관)

      • 어떤 타입을 전달받아서 해당 함수 등 에서 사용 가능하게 함.

      • 사용하는 쪽에서 타입 결정

        특정 타입으로 강제하고 싶을 경우 : <string | number> 사용 가능

    • 매개 변수의 타입

      ex) T[]

📌 사용 효과

클래스나 함수, 인터페이스를 다양한 타입으로 재사용 가능

📌 사용 비교

🔹 사용하지 않을 경우

  • 매개변수의 타입이 바뀌었는데 동일한 함수를 재사용하려면

    → 함수 오버로드 or 유니온 타입 사용 가능.

    → 다른 타입들을 배열로 만들어 전달할 때마다 계속 적어줘야 함.

  • 이럴 때 제네릭이 효과적.

function getSize(arr: number[] | string[] | boolean[] | object[]): number { // 👈 파라미터 길어질 것
  return arr.length;
}

const arr1 = [1,2,3];
getSize(arr1); // 3

const arr2 = ["a", "b", "c"]
getSize(arr2) // 3

const arr3 = [true, false, true]
getSize(arr3);

const arr4 = [{}, {}, { name: "Tim" }]
getSize(arr4);

🔹 사용

function getSize<T>(arr: T[]): number { // 👈 타입 파라미터 & 매개 변수의 타입
  return arr.length;
}

const arr1 = [1,2,3];
getSize<number>(arr1); // 3
// [T 부분 모두 number] function getSize<number>(arr: number[]): number

const arr2 = ["a", "b", "c"]
getSize<string | number>(arr2) // 3
// [특정 타입으로 강제]

const arr3 = [true, false, true]
getSize(arr3); 
// [타입 파라미터 없어도 타입 파악] function getSize<boolean>(arr: boolean[]): number

const arr4 = [{}, {}, { name: "Tim" }]
getSize(arr4);

📌 interface

제네릭을 활용해서 하나의 interface를 선언 후, 다양한 객체들 제작 가능

🔹 사용 전

사용 가능한 모든 타입을 적는 것 → 비효율적

interface Mobile {
  name: string;
  price: number;
  option: any;
}

// option에 어떤 데이터가 들어올지 모르는 상태. -> 이럴 때 제네릭 사용 가능

🔹 사용 후

interface Mobile<T> {
  name: string;
  price: number;
  option: T;
}

const m1: Mobile<{color: string; coupon: boolean}> = {
  name: "s21",
  price: 1000,
  option: {
    color: "red",
    coupon: false,
  }
}
/*
타입 파라미터
- <object>도 가능 (객체로 이루어져있기 때문)
- option 객체의 모습이 정해져있다면 위와 같이 사용 가능 */

const m2: Mobile<string> = { 
  name: "s20",
  price: 900,
  option: "good"
}
/*
타입 파라미터
- 없으면 에러
- option의 타입이 string이니까 string 사용 */

📌 extends

🔹 예시

◽ 문제

interface User {
  name: string;
  age: number;
}

interface Car {
  name: string;
  color: string;
}

interface Book {
  price: number;
}

const user: User = {name: "a", age: 20};
const car: Car = {name: "bmw", color: "red"};
const book: Book = {price: 1000}; // 👈 name 無

function showName(data): string { // 👈 [에러 발생] 매개변수(data) 타입이 any.
  return data.name; // 👈 book에 name 없어서 사용 불가.
}

showName(user);
showName(car);
showName(book);

◽ 해결 1 | 매개 변수에 타입 추가 (제네릭 이용)

(...생략)

function showName<T>(data: T): string { // 👈 
  return data.name;
	/* 
  👆 name 에러 발생
  이유: T에는 name이 없다. 
	(현재 전달되는 것(user나 car. name 有)만 보고 
	모든 매개변수에 name이 있다고 장담 불가. -> 에러 발생.) */
}

(...생략)

◽ 해결 2 | extends 활용

(...생략)

function showName<T extends {name: string}>(data: T): string { // 👈
  return data.name;
}

showName(user);
showName(car);
showName(book); // 👈 book 에러 발생 (이유: name 없음)

<T extends {name: string}>(data: T)

  • 의미

    어떤 T 타입이 올건데 그 타입은 namestring인 객체를 확장한 형태이다.

  • 효과

    다양한 객체가 와도 항상 namestring을 가짐.

    name이 없거나 string이 아니라면 → 에러 표시 (ex. showName(book) 에러)

  • name 의 타입이 다를 경우

    interface User {
      name: string;
      age: number;
    }
    
    interface Car {
      name: boolean; // 👈 boolean으로 바꿈
      color: string;
    }
    
    interface Book {
      price: number;
    }
    
    const user: User = {name: "a", age: 20};
    const car: Car = {name: true, color: "red"}; // 👈 true로 바꿈
    const book: Book = {price: 1000};
    
    function showName<T extends {name: string}>(data: T): string {
      return data.name;
    }
    
    showName(user);
    showName(car); // 👈 car 에러 발생. 이유: string이 와야 함.
    // showName(book);

참고

  • 코딩앙마_타입스크립트
profile
복습 목적 블로그 입니다.

0개의 댓글