ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Typescript - 제네릭
    Typescript 2023. 4. 20. 21:25
    728x90

    제네릭 함수

    이제까지는 그냥 타입을 지정하고 받는 것들을 해봤다. 그런데 우리가 코드를 작성하면서 항상 타입이 지정된 것만 사용할 수 있을까?

    또한 우리가 지금까지 object가 너무 추상적이여서 쓰는데 어려움을 겪었는데 그 문제를 해결할 수 있는 한가지 방법이 바로 제네릭 함수이다.

    예제코드는 다음과 같다.

    const echo = <T>(a: T): T => {
      console.log(a);
      return a;
    };
    
    echo(10);
    echo("asdf");
    echo({ name: "test" });
    echo(["123"]);

    이렇게 코드가 있을때 여기서 중요한 것은 알파벳 T가 아니다. T의 경우는 A로 써도 되고 저기서 중요한 것은 양식이다. 왜냐하면 저 양식을 통해

    제네릭 함수를 사용한다는 것을 알면 되기 때문이다.

    저 양식에 어떤 알파벳이 들어가도 무방하지만 결국 T를 쓰는 이유는 바로 타입을 뜻하기 때문이다. 이 부분은 필히 익숙해져야 하기 때문에

    코드를 통해 좀 더 살펴보자.

    interface Props {
      name: string;
      id: string;
    }
    
    const echo = <T>(a: T): T => {
      console.log(a);
      return a;
    };
    
    const props: Props = {
      name: "ingoo",
      id: "web7722",
    };
    
    echo(props);

    이렇게 작성한 코드의 추정을 살펴보면 const props: Props

    로 정확히 추정하고 있는 것을 볼 수 있다.

    하지만 타입을 제거하고 다시 코드를 살펴보면

    interface Props {
      name: string;
      id: string;
    }
    
    const echo = <T>(a: T): T => {
      console.log(a);
      return a;
    };
    
    const props = {
      name: "ingoo",
      id: "web7722",
    };
    
    echo(props);

    이럴 경우 echo 메서드의 추정은

    const echo: <{
        name: string;
        id: string;
    }>(a: {
        name: string;
        id: string;
    }) => {
        name: string;
        id: string;
    }

    인 것을 볼 수 있다. 이를 해결하기 위한 방법으로는 함수를 호출할떄 타입을 인자값으로 받을수 있게끔 해주면 된다

    interface Props {
      name: string;
      id: string;
    }
    
    const echo = <T>(a: T): T => {
      console.log(a);
      return a;
    };
    
    const props = {
      name: "ingoo",
      id: "web7722",
    };
    
    echo<Props>(props);

    이렇게 하고 다시 echo의 메서드를 확인하면 Props인것을 확인 할 수 있다.

    만약에 Props 관련되지 않은 속성을 추가하면 에러가 발생하게 되므로 조심하도록 하자.

    제네릭 함수가 주로 사용되는 부분은 객체, 특히 배열에서 많이 사용된다는 것을 기억하도록 하자.

    다음 예제를 살펴보자.

    const arr: string[] = ["asdf", "asfg", "asdg"];
    const push = () => {};

    우리가 배열을 선언할떄는 저런식으로 작성을 해줘야지만 오류가 나지 않았다. 근데 이제 매개변수에 어떤 것을 받던간에 배열이라는 것을 알려주는데 사용이 가능하다.

    const push = <T>(a: T): T[] => {
      const result = [a];
      console.log(result);
    
      return result;
    };
    
    push(1);
    push("asdf");

    여기서 가장 큰 장점으로는 안의 배열안의 인자값이 무엇이든 간에 오류를 뜨우지 않고 문제를 해결했다는 것이다.

    아까 이제 우리가 만든 코드를 좀 더 간단하게 만들어보자.

    원래코드;
    const reverse2 = (x: string | number): string | number => {
      const res = x.toString().split("").reverse().join("");
      return typeof x === "number" ? parseInt(res) : res;
    };
    
    console.log(reverse2(123), reverse2("abc"));

    이 코드를 제네릭을 사용하면 더 간단하게 만들 수 있는데 먼저 많이 실수 하는 코드에 대해서 살펴보자

    const reverse2 = <T>(x: T): T => {
      const res = x.toString().split("").reverse().join("");
      return typeof x === "number" ? parseInt(res) : res;
    };
    
    console.log(reverse2(123), reverse2("abc"));

    이렇게 코드를 작성할 경우 x에 올 수 있는 가짓수가 너무 많아져서 toString()이 안되는 type도 올 수 있다고

    컴퓨터는 파악한다. 따라서 작성이 안되는 것이다. 때문에 위에 조건을 걸어줘야 한다.

    const reverse2 = <T>(x: T): T => {
      if (typeof x === "number" || typeof x === "string") {
        const res = x.toString().split("").reverse().join("");
        return typeof x === "number" ? (parseInt(res) as T) : (res as T);
      }
      return x;
    };
    
    console.log(reverse2(123), reverse2("abc"));

    number나 string일 경우에만 코드가 동작하도록 코드를 구성했다. 이때 우리가 꼭 알고 있어야 하는 부분은

    제네릭으로 리턴값을 지정하면 무조건 return 값을 제네릭으로 설정해야 한다는 것이다.

    그래야지만 제네릭 설정이 들어가게 되고 정확히 우리가 원하는 제네릭 기능을 해석할 수 있다.

    제네릭을 주로 써야 하는 이유에 대해서 다시 간략하게 이야기 하자면

    다음과 같은 interface들이 존재한다고 해보자.

    interface A {
      name: string;
    }
    
    interface B {
      age: number;
    }
    
    interface C {
      weight: number;
    }
    
    const a: A = { name: "asdf" };
    const b: B = { age: 30 };
    const c: C = { weight: 100 };
    
    const objectToArray = (value: object) => {
      return Object.entries(value);
    };
    
    console.log(objectToArray(a));

    이럴떄 그냥 쓰면 object는 너무 상위의 개념이기 때문에

    우리가 object에서 원하는, 즉 객체의 메서드나, 객체의 키들을 불러오지 못하는 불편함이 있었다.

    이를 해결하는데에 도움이 되는것이 제네릭이다.

    interface A {
      name: string;
    }
    
    interface B {
      age: number;
    }
    
    interface C {
      weight: number;
    }
    
    const a: A = { name: "asdf" };
    const b: B = { age: 30 };
    const c: C = { weight: 100 };
    
    const objectToArray = <T>(value: T): T => {
      return value;
    };
    
    console.log(objectToArray<B>(b).age);

    이런식으로 해결이 되는 것을 볼 수 있다.

    'Typescript' 카테고리의 다른 글

    Typescript - SOLID 예제  (0) 2023.04.21
    Typescript - SOLID  (0) 2023.04.20
    Typescript - 함수 오버로딩  (0) 2023.04.20
    Typescript - 자주 사용하는 TS형식에 관해  (0) 2023.04.20
    Tyepscript - 예제코드2/예제코드3  (0) 2023.04.20

    댓글

Designed by Tistory.