본문 바로가기

typeScript Generic / Utility 본문

개발/nest.js

typeScript Generic / Utility

자전하는명왕성 2023. 1. 31. 23:47

Generic

제네릭을 사용하면 다양한 방식으로 자신만의 타입을 사용할 수 있다.

제네릭은 Type 을 함수의 파라미터처럼 사용할 수 있는 것을 의미하는데,

기본 타입과 Generic 을 적용한 타입을 비교해보고자 한다.

 

기본 타입

문자 / 숫자 / 불리언 || any || unknown

// 1. 문자/숫자/불린 기본 타입
const getPrimitive = (arg1: string, arg2: number, arg3: boolean): [boolean, number, string] => {
  return [arg3, arg2, arg1]; // type 으로 정해놓은 값만 반환이 가능하다.
};
const result = 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 result = getAny("철수", 123, true);
// 하지만, 타입스크립트는 그말마따나 목적이 '타입'을 정해주는 것에 있기 때문에 사용하지 않는 편이 좋다.

//
//

// 3. unknown 타입 // 타입을 모를 때는 unknown 을 통해 원하는 결과에 해당 타입을 유추해 명시해준다.
const getUknown = (arg1: unknown, arg2: unknown, arg3: unknown): [unknown, unknown, unknown] => {
  if (typeof arg1 === "number") console.log(arg1 + 100); 
  return [arg3, arg2, arg1];
};
const result = getUknown("철수", 123, true);

정석으로 보이는 typescript 사용 방법이라고 볼 수 있으나, 범용성이 떨어지는 편이다.

특히 any 의 경우 타입을 제한할 수 없을 뿐더러, 어떤 타입의 데이터가 리턴되는지 알 수 없다는 문제점도 있다.

이런 문제점 때문에 등장한 것이 Generic.

 

generic 타입은 어떤 타입인지는 모르지만 다양한 타입에서 작동하는 코드를 작성할 수 있다.

// generic 타입 < >
function getGeneric<MyType1, MyType2, MyType3>(arg1: MyType1, arg2: MyType2, arg3: MyType3): [MyType3, MyType2, MyType1] {
  return [arg3, arg2, arg1];
}
const result = getGeneric<string, number, boolean>("철수", 123, true);

//
//

// generic 타입 2 // T의 경우 generic 선언 시 자주 타입변수로써 범용적으로 사용한다.
function getGeneric2<T1, T2, T3>(arg1: T1, arg2: T2, arg3: T3): [T3, T2, T1] {
  return [arg3, arg2, arg1];
}
const result = getGeneric2<string, number, boolean>("철수", 123, true);

//
//
// generic 타입 3 꼭 T가 아니라, 어떤 문자를 사용하든 상관 없다.
function getGeneric3<T, U, V>(arg1: T, arg2: U, arg3: V): [V, U, T] {
  return [arg3, arg2, arg1];
}
const result = getGeneric3<string, number, boolean>("철수", 123, true);


// generic 타입 화살표 함수
const getGeneric4 <T, U, V>(arg1: T, arg2: U, arg3: V): [V, U, T] => {
    return [arg3, arg2, arg1];
  }
const result = getGeneric4("철수", 123, true);

중요한 건, 꺽쇠를 사용하여 제네릭 타입임을 명시해주어야 타입스크립트가 들어온 데이터의 타입을 추론할 수 있게 된다.

화살표 함수로도 활용할 수 있다는 것을 기억해두자.

 

 

Utility

유틸리티란 공통적인 타입을 변환하는데에 용이하게 사용하는 데에 도움을 준다.

대표적인 utility 를 소개한다.

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

// 1. partial 타입 (필수 요소 해제)
type aaa = Partial<IProfile>;
// IProfile의 모든 요소들이 필수적으로 요구하는 값이 아니게 된다. 
// 'hobby?' 과 같은 특성을 갖게 된다는 의미.

// 2. required 타입 (모든 요소 필수)
type bbb = Required<IProfile>;
// IProfile 의 모든 요소가 partial과 반대로 모든 값이 필수 요구하는 값이 된다.

// 3. pick 타입 (필요한 요소 선택 / 이름 나이)
type ccc = Pick<IProfile, "name" | "age">;
// 원하는 요소만 선택할 수 있게 된다.

// 4. omit 타입 (불필요한 요소 제외 / 스쿨 제외)
type ddd = Omit<IProfile, "school">;
// pick 과 반대.

// 5. Record 타입 // 다른 타입에 매핑시키는데 사용한다. 
type eee = "철수" | "영희" | "훈이"; // union 타입 / key : value 로 사용 가능
let child: eee = "영희"; // union 타입으로 가져오기 때문에 '철수, 영희, 훈이'만 가능

type fff = Record<eee, IProfile>; // Record 타입으로 eee에 IProfile 타입 적용

// 6. 객체의 Key들로 union 타입 만들기
type ggg = keyof IProfile; // 'name' | 'age' | 'school' | 'hobby'
let myprofile: ggg = "hobby";

// 7. type vs interface 의 차이 => interface는 선언병합 가능
interface IProfile {
  candy: number; // 선언 병합으로 추가됨
}
// 8. 배운 내용 응용
let profile: Partial<IProfile> = {
  candy: 10
};
Comments