[TS] Utility Type을 알아보자!

imzzuu·2022년 11월 28일
0
post-thumbnail

저번 면접때 유틸리티타입에 관한 질문을 받았었다.
타입스크립트를 이제 막 접했던 나에게는 생소한 타입이었는데
면접관님께서 Utility Type이 실제로 어떤 동작원리를 갖는지 코드 편집기의 레퍼런스를 보며 공부하면 좋을 것 같다는 피드백을 주셔서 이번 기회를 통해 제대로 파헤쳐보았다!

먼저, 유틸리티 타입에 대해 알아보기 전에 레퍼런스 코드를 보다보면
in, keyof, in keyof 문법을 자주 보게되는데,,,,
동작을 이해하려면 이것부터 정리하고 갈 필요가 있었다.

in

자바스크립트의 for in 과 같이 순회하여 실행하는 문법이다.

type Heroes = 'Hulk' | 'Thor' | 'Capt';

type HeroProfiles = { [K in Heroes]: number };
const heroInfo: HeroProfiles = {
  Hulk: 54,
  Thor: 1000,
  Capt: 33,
}

Heroes 타입의 세가지 문자열을 순회하여 key로 만들고 타입을 number로 지정하는 heroInfo의 타입을 정의하였다.

  • 동작
    { Hulk: number } // 첫번째 순회
    { Thor: number } // 두번째 순회
    { Capt: number } // 세번째 순회
    type HeroProfiles = {
      Hulk: number;
      Thor: number;
      Capt: number;
    }

keyof

타입 T 의 모든 key 값을 Union Type으로 가져오는 문법

interface User {
  id: number;
  name: string;
  age: number;
  gender: "m" | "f";
}

type UserKey = keyof User; // 'id' | 'name' | 'age' | 'gender'

const uk: UserKey = "id"; // User의 키 값 중 한개를 사용하면 오류가 안남

in keyof

keyof T에 속하는 모든 프로퍼티를 순회하는 문법

type Partial<T> = { [P in keyof T]?: T[P]; }

해당 프로퍼티의 Value를 P로 가져와서 새로운타입을 만든다.

이렇게 레퍼런스에서 주로 보이는 기본 문법을 알아보았다면, 본격적으로 유틸리티 타입에 대해 하나씩 알아보자!!



📌 Partial

: T의 모든 프로퍼티를 옵셔널 프로퍼티로 변경한 새로운 타입을 반환

type Partial<T> = {
    [P in keyof T]?: T[P];
};

예시

interface User {
  id: number;
  name: string;
  age: number;
  gender: "m" | "f";
}

let admin: Partial<User> = {
  id: 1,
  name: "Bob",
};

User 타입의 모든 프로퍼티가 필수로 들어가지 않아도 오류가 나지 않는다!
즉, 타입의 모든 프로퍼티를 옵셔널 (?) 로 바꿔주는 것이다.

interface User {
  id?: number;
  name?: string;
  age?: number;
  gender?: "m" | "f";
}

코드로 작성하면 이렇다.


📌 Required

: T의 모든 프로퍼티를 필수 프로퍼티로 변경한 새로운 타입을 반환

type Required<T> = {
    [P in keyof T]-?: T[P];
};

예시

interface User {
  id: number;
  name: string;
  age?: number; // 옵셔널이지만
}

let admin2: Required<User> = {
  id: 1,
  name: "Bob",
  age: 30, // 필수로 바뀌어버림!
};

옵셔널을 제거해버린다!


📌 Readonly

: T의 모든 프로퍼티를 읽기 전용으로 변경한 새로운 타입을 반환

type Readonly<T> = {
    readonly [P in keyof T]: T[P];
};

예시

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

let admin3: Readonly<User> = {
  id: 1,
  name: "Bob",
};

admin3.id = 4; // error : 읽기전용 속성으로 변경되어 재할당 불가능!

📌 Record<K,T>

: 오브젝트의 각 프로퍼티마다 키는 K 타입 중 하나이고, 값은 T 타입인 새로운 타입을 반환

type Record<K extends keyof any, T> = {
   [P in K]: T;
};

예시1

interface MovieInfo {
  rank: number;
  director: string;
}
 
type MovieTitle = "Spiderman" | "Dune2" | "Avatar2";
 
const cats: Record<MovieTitle, MovieInfo> = {
  Spiderman: { rank: 10, director: "Jon Watts" },
  Dune2: { rank: 5, director: "Denis Villeneuve" },
  Avatar2: { rank: 16, director: "James Cameron" },
};

예시2

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

function isVaild(user: User) {
  const result: Record<keyof User, boolean> = {
    id: user.id > 0,
    name: user.name !== "",
    age: user.age > 0,
  };
  return result;
}

📌 Pick<T,K>

: 타입 T의 지정한 Key로만 구성된 프로퍼티를 갖는 새로운 타입을 반환

type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
};

예시

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

const admin5: Pick<User, "id" | "name"> = {
  id: 0,
  name: "sam",
};

📌 Omit<T,K>

: 타입 T의 지정한 Key를 제외한 프로퍼티를 갖는 새로운 타입을 반환

type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

예시

interface User {
  id: number;
  name: string;
  age: number;
  gender: "m" | "f";
}

const admin6: Omit<User, "age" | "gender"> = {
  id: 0,
  name: "sam",
};

📌 Exclude<T, U>

: 타입 T와 타입 U의 차집합의 타입을 갖는 새로운 타입을 반환

type Exclude<T, U> = T extends U ? never : T;

✨ Omit과 비슷해보이지만, Omit은 프로퍼티의 차집합, Exclude 는 타입의 차집합이다.


예시

type T1 = string | number | boolean;
type T2 = Exclude<T1, number | string>; // boolean 

📌 NonNullable

: null 과 undefined를 제외한 타입을 갖는 새로운 타입을 반환

type NonNullable<T> = T extends null | undefined ? never : T;

예시

type T3 = string | null | undefined | void;
type T4 = NonNullable<T3>; // string | void


[참조]
https://typescript-kr.github.io/pages/utility-types.html, https://seokzin.tistory.com/entry/TypeScript-유틸리티-타입-구현하기-Utility-Types, https://driip.me/75ce196d-484d-48b4-9108-b7d4f81879a6

profile
FrontenDREAMER

0개의 댓글