-
Typescript - 제네릭Typescript 2023. 4. 20. 21:25728x90
제네릭 함수
이제까지는 그냥 타입을 지정하고 받는 것들을 해봤다. 그런데 우리가 코드를 작성하면서 항상 타입이 지정된 것만 사용할 수 있을까?
또한 우리가 지금까지 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