본문 바로가기

온라인 강의(유데미, 인프런 등)/한 입 크기로 잘라먹는 타입스크립트(인프런)

[한 입 크기로 잘라먹는 타입스크립트] 유틸리티 타입

반응형

1. 유틸리티 타입이란

유틸리티 타입이란 타입스크립트가 자체적으로 제공하는 특수한 타입들입니다. 제네릭, 맵드 타입, 조건부 타입 등의 타입 조작 기능을 이용해 실무에서 자주 사용되는 유용한 타입들을 모아 놓은 것을 의미한다.

다음과 같이 Readonly<T>와 같은 유틸리티 타입을 이용해 특정 객체 타입의 모든 프로퍼티를 읽기 전용 프로퍼티로 변환할 수 있다.

interface Person {
  name : string;
  age : number;
}

const person : Readonly<Person> ={
  name : "이정환",
  age : 27
}

person.name = ''
// ❌ name은 Readonly 프로퍼티입니다.

또는 다음과 같이 Partial<T> 유틸리티 타입을 이용해 특정 객체 타입의 모든 프로퍼티를 선택적 프로퍼티로 변환하는 것도 가능하다. 타입스크립트는 굉장히 다양한 유틸리티 타입을 제공한다.

interface Person {
  name: string;
  age: number;
}

const person: Partial<Person> = {
  name: "이정환",
};

아래의 타입스크립트 공식문서 에서 다양한 유틸리티 타입들을 확인할 수 있다.

https://www.typescriptlang.org/docs/handbook/utility-types.html

자주 활용되는 몇가지 핵심 유틸리티 타입만 추려서 알아보자.

 

2. Partial, Required, Readonly

1) Partial

Partial은 부분적인 또는 일부분의 라는 뜻으로 특정 객체 타입의 모든 프로퍼티를 선택적 프로퍼티로 변환한다. 따라서 기존 객체 타입에 정의된 프로퍼티들 중 일부분만 사용할 수 있도록 도와주는 타입이다.

interface Post {
  title: string;
  tags: string[];
  content: string;
  thumbnailURL?: string;
}

const draft: Partial<Post> = {
  title: "제목 나중에 짓자",
  content: "초안...",
};

// 구현
type Partial<T> = {
  [key in keyof T]?: T[key];
};

2) Required

Required는 우리말로 필수의, 필수적인 이라는 뜻으로 특정 객체 타입의 모든 프로퍼티를 필수(선택적이지 않은) 프로퍼티로 변환한다.

interface Post {
  title: string;
  tags: string[];
  content: string;
  thumbnailURL?: string;
}

(...)

const withThumbnailPost: Required<Post> = { // ❌
  title: "한입 타스 후기",
  tags: ["ts"],
  content: "",
  // thumbnailURL: "https://...",
};

// 구현
// -? 는 ?가 붙어있는 선택적 프로퍼티가 있으면 ?를 제거하라는 의미
type Required<T> = {
  [key in keyof T]-?: T[key];
};

 

3) Readonly

Readonly는 우리말로 읽기 전용 이라는 뜻으로 특정 객체 타입의 모든 프로퍼티를 읽기 전용 프로퍼티로 변환한다.

interface Post {
  title: string;
  tags: string[];
  content: string;
  thumbnailURL?: string;
}

(...)

const readonlyPost: Readonly<Post> = {
  title: "보호된 게시글입니다.",
  tags: [],
  content: "",
};

readonlyPost.content = '해킹당함'; // ❌

// 구현
type Readonly<T> = {
  readonly [key in keyof T]: T[key];
};

 

3. Record, Pick, Omit

1) Pick

Pick은 우리말로 뽑다, 고르다 라는 뜻이다. 따라서 특정 객체 타입으로부터 특정 프로퍼티 만을 골라내는 타입이다. 예를 들어 Pick 타입에 T가 name, age가 있는 객체 타입이고 K가 name 이라면 결과는 name만 존재하는 객체 타입이 된다.

변수 legacyPost의 타입으로 Pick<Post, "title" | "content">을 정의다. 따라서 이때 타입변수 T에는 Post가 타입변수 K에는 “title” | “content” 이 각각 할당된다. 그럼 Post 타입으로부터 “title”“content” 프로퍼티만 쏙 뽑아낸 객체 타입이 된다.

interface Post {
  title: string;
  tags: string[];
  content: string;
  thumbnailURL?: string;
}

(...)

const legacyPost: Pick<Post, "title" | "content"> = {
  title: "",
  content: "",
};
// 추출된 타입 : { title : string; content : string }

// 구현
// K가 T의 key로만 이루어진 String Literal Union 타입임을 보장해야 한다.
type Pick<T, K extends keyof T> = {
  [key in K]: T[key];
};

2) Omit

Omit은 우리말로 생략하다, 빼다 라는 뜻이다. 따라서 특정 객체 타입으로부터 특정 프로퍼티 만을 제거하는 타입이다. 예를 들어 Omit 타입에 T가 name, age가 있는 객체 타입이고 K가 name 이라면 결과는 name을 제외하고 age 프로퍼티만 존재하는 객체 타입이 된다.

const noTitlePost: Omit<Post, "title"> = {
  content: "",
  tags: [],
  thumbnailURL: "",
};

 

keyof T는 ‘title’ | ‘content’ | ‘tags’ | ‘thumbnailURL’이므로 Pick<T, Exclude<keyof T, K>>은 Pick<Post, Exclude<'title' | 'content' | 'tags' | 'thumbnailURL' , 'title>> 이 된다.

Exclude 타입은 2개의 타입 변수를 할당받는데 T로부터 K를 제거한다. 따라서 한번 더 변환하면 다음과 같다.

Pick<Post, 'content' | 'tags' | 'thumbnailURL'>

결과적으로 Post에서 content, tags, thubmnailURL 프로퍼티만 존재하는 객체 타입이 된다. 따라서 K에 전달한 ‘title’이 제거된 타입을 얻을 수 있다.

3) Record

다음과 같이 화면 크기에 따라 3가지 버전의 썸네일을 지원한다고 가정하고 Thumbnail 타입을 별도로 정의해보자.

type Thumbnail = {
  large: {
    url: string;
  };
  medium: {
    url: string;
  };
  small: {
    url: string;
  };
};

그런데 여기에 watch 버전이 또 추가되어야 하면 그럼 다음과 같이 똑같이 생긴 프로퍼티를 하나 더 추가해줘야 한다. 앞으로 버전이 많아질 수록 계속해서 중복코드가 발생하게 될 것이다.

이럴 때 Record를 이용한다. 다음과 같이 K에는 어떤 프로퍼티들이 있을지 String Literal Union 타입을 할당하고 V에는 프로퍼티의 값 타입을 할당한다.

type Thumbnail = Record<
  "large" | "medium" | "small",
  { url: string }
>;

//구현
type Record<K extends keyof any, V> = {
  [key in K]: V;
};

위 Record 타입은 K에는 “large” | “medium” | “small”이 할당되었으므로 large, medium, small 프로퍼티가 있는 객체 타입을 정의하고, 각 프로퍼티 value의 타입은 V에 할당한 { url : stirng } 이 된다.

 

4. Exclude, Extract, ReturnType

1) Exclude

Exclude 타입은 다음과 같이 T로부터 U를 제거하는 타입이다.

type A = Exclude<string | boolean, boolean>;
// string

//구현
type Exlcude<T, U> = T extends U ? never : T;

2) Extract

Extract 타입은 다음과 같이 T로 부터 U를 추출하는 타입이다.

type B = Extract<string | boolean, boolean>;
// boolean

// 구현
type Extract<T, U> = T extends U ? T : never;

3) ReturnType

ReturnType은 타입변수 T에 할당된 함수 타입의 반환값 타입을 추출하는 타입이다.

type ReturnType<T extends (...args: any) => any> = T extends (
  ...agrs: any
) => infer R
  ? R
  : never;

function funcA() {
  return "hello";
}

function funcB() {
  return 10;
}

type ReturnA = ReturnType<typeof funcA>;
// string

type ReturnB = ReturnType<typeof funcB>;
// number

 

https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8?inst=68d752f7&utm_source=instructor&utm_medium=referral&utm_campaign=inflearn_%ED%8A%B8%EB%9E%98%ED%94%BD_promotion-link 

 

한 입 크기로 잘라먹는 타입스크립트 - 인프런 | 강의

문법을 넘어 동작 원리와 개념 이해까지 배워도 배워도 헷갈리는 타입스크립트 이제 제대로 배워보세요! 여러분을 타입스크립트 마법사🧙🏻‍♀️로 만들어드립니다., - 강의 소개 | 인프런

www.inflearn.com

 

반응형