Generic Types

JBoB·2023년 1월 31일
0

🐧Generic이란?

제네릭은 언어에서 재사용성이 높은 컴포넌트를 만들 때 자주 활용되는 특징이다

제네릭은 타입을 마치 함수의 파라미터처럼 사용하는 것을 의미한다.

특히, 한가지 타입보다 여러가지 타입에서 동작하는 컴포넌트를 생성하는 데 사용된다.

아래 코드는 인자를 하나 넘겨 받아 반환해주는 함수입니다.

여기서 이 함수의 인자와 반환 값은 모두 string으로 지정되어 있습니다.

function getText<string>(text: string): string {
  return text;
}

만약 여러 가지 타입을 허용하고 싶다면 아래와 같이 any를 사용할 수 있습니다.

function logText(text: any): any {
  return text;
}

이렇게 타입을 바꾼다고 해서 함수의 동작에 문제가 생기진 않습니다.

다만, 함수의 인자로 어떤 타입이 들어갔고 어떤 값이 반환되는지는 알 수가 없습니다.

왜냐하면 **any라는 타입은 타입 검사를 하지 않기 때문**입니다.

이러한 문제점을 해결할 수 있는 것이 제네릭입니다. 아래 코드를 보겠습니다.

function logText<T>(text: T): T {
  return text;
}

먼저 함수의 이름 바로 뒤에 <T> 라는 코드를 추가했습니다.

그리고 함수의 인자와 반환 값에 모두 T 라는 타입을 추가합니다.

이렇게 되면 함수를 호출할 때 넘긴 타입에 대해 타입스크립트가 추정할 수 있게 됩니다.

따라서, 함수의 입력 값에 대한 타입과 출력 값에 대한 타입이 동일한지 검증할 수 있게 됩니다.

그리고 이렇게 선언한 함수는 아래와 같이 2가지 방법으로 호출할 수 있습니다.

// #1
const text = logText<string>("Hello Generic");
// #2
const text = logText("Hello Generic");

보통 두 번째 방법이 코드도 더 짧고 가독성이 좋기 때문에 흔하게 사용됩니다.

그렇지만 만약 복잡한 코드에서 두 번째 코드로 타입 추정이 되지 않는다면 첫 번째 방법을 사용하면 됩니다.

🐤타입 종류

// 1.문자/숫자/불린 기본타입
const getPrimitive = (arg1: string, arg2: number, arg3: boolean): [boolean, number, string] => {
  return [arg3, arg2, arg1];
};

const result1 = getPrimitive("철수", 123, true);

//
//
// 2. any 타입(자바스크립트와 동일)
const getAny = (arg1: any, arg2: any, arg3: any): [any, any, any] => {
  console.log(arg1 + 100); // any 는 아무거나 다 됨!
  return [arg3, arg2, arg1];
};

const result2 = getAny("철수", 123, true);

//
//
// 3. unknown 타입
const getUnknown = (arg1: unknown, arg2: unknown, arg3: unknown): [unknown, unknown, unknown] => {
  if (typeof arg1 === "number") console.log(arg1 + 100); // any 보다는 안전
  return [arg3, arg2, arg1];
};

const result3 = getUnknown("철수", 123, true);

//
//
// 4-1. generic 타입(들어오는 타입에 따라서 MyType이 설정된다.)
function getGeneric<MyType1, MyType2, MyType3>(arg1: MyType1, arg2: MyType2, arg3: MyType3): [MyType3, MyType2, MyType1] {
  return [arg3, arg2, arg1];
}

const result4 = getGeneric("철수", 123, true);

//
//
// 4-2. generic 타입(들어오는 타입을 고정시키기 위해서는 아규먼트에 <> 추가 해줘야한다.)
function getGeneric2<MyType1, MyType2, MyType3>(arg1: MyType1, arg2: MyType2, arg3: MyType3): [MyType3, MyType2, MyType1] {
  return [arg3, arg2, arg1];
}

const result5 = getGeneric2<string, number, boolean>("철수", 123, true);

//
//
// 4-3. generic 타입
function forRoot<T1, T2, T3>(arg1: T1, arg2: T2, arg3: T3): [T3, T2, T1] {
  return [arg3, arg2, arg1];
}

const result6 = forRoot("철수", 123, true);

//
//
// 4-4. generic 타입
function getGeneric4<T, U, V>(arg1: T, arg2: U, arg3: V): [V, U, T] {
  return [arg3, arg2, arg1];
}
const result7 = getGeneric4<string, number, boolean>("철수", 123, true);

//
//
// 4-5. generic 타입 - 4 (화살표 함수 사용)
const getGeneric5 = <T, U, V>(arg1: T, arg2: U, arg3: V): [V, U, T] => {
  return [arg3, arg2, arg1];
};
const result8 = getGeneric5<string, number, boolean>("철수", 123, true);

Generic Type은 우리가 함수를 만들어서 제공해 줄 때 안전하고 확장성 높은 코드 사용을 위해 많이 사용한다.

중요

꺽새를 항상 사용하여 임의의 함수를 지정해주기.

타입 명시를 해주어 에러 발생을 막아주기.

🐧Utility Type

Utility Type은 기존에 있던 Type들을 변형해서 변형된 타입을 새로 만들어주는 역할을 하며,

코드의 가독성을 높이기 위해(같은 코드를 여러번 작성하지 않기 위해) 많이 사용한다.

interface IProfile {
  name: string;
  age: number;
  school: string;
  hobby?: string;
}

//1. Partial 타입 (부분:있어도 되고 없어도 되는)
type aaa = Partial<IProfile>;

//2. Required 타입 (필수사항)
type bbb = Required<IProfile>;

//3. Pick 타입 (원하는 속성만 뽑아서 사용하고 싶을 때)
type ccc = Pick<IProfile, "name" | "age">;

//4. Omit 타입 (원하는 속성 제거하여 사용하고 싶을 때 )
type ddd = Omit<IProfile, "school">;

//5. Record 타입
type eee = "철수" | "영희" | "훈이"; // Union 타입
let child1: eee = "철수"; // 철수,영희,훈이만 됨
let child2: string = "사과"; // 철수 영희 훈이 사과 바나나 다됨

type fff = Record<eee, IProfile>; // Record 타입

//6.객체의 키들로 Union 타입 만들기
type ggg = keyof IProfile; // "name" | "age" | "school" | "hobby"의 Union타입을 만들어줌
let myprofile: ggg = "hobby";

//7. type vs interface 차이  => interface는 선언병합 가능
interface IProfile {
  candy: number; // 선언병합으로 추가됨
}

// 8.배운것 응용
let profile: Partial<IProfile> = {
  candy: 10,
};

위의 예시만 본다면 이해가 가지만 Union 타입Record 타입 는 이해가 안 갈 수도 있다.

이 두가지에 대해 알아보도록 하자.

Union 타입

:Javascript의 OR 연산자( || )와 같이 ‘A’ 이거나 ‘B’이다 라는 의미의 타입으로,

**|** 연산자를 이용하여 타입 또는 값을 여러 개 연결할 때 사용할 수 있다.

유니온타입으로 key값을 저장해주면 다른 변수에 할당에 주면 그 유니온 타입으로 지정된 key값만 사용할 수 있다.

Record 타입

// index.ts

interface IProfile {
  name: string;
  age: number;
  school: string;
  hobby?: string;
}

// 5. Record 타입
type eee = "철수" | "영희" | "훈이"; // Union 타입
type fff = Record<eee, IProfile>; // Record 타입

Union Type 속성을 다른 Type으로 매핑 시키고자 할 때 사용한다.

위의 예제를 보면 유니온 타입으로 철수 영희 훈이 의 key 값을 받아오고 Record<> 을 통해

IProfile 를 value 값으로 매핑 된 것을 확인 할수 있다.

profile
간절하고 치열하게 살자

0개의 댓글