티스토리 뷰

TS | NestJS

[TS] 유틸리티(Utility) 타입

rimo (리모) 2024. 3. 2. 20:43

 

아래 글에 이어 작성된 내용입니다.

 

 

[TS] TypeScript의 타입 조작

아래 글에 이어 작성된 내용입니다. [TS] TypeScript의 기본 타입 아래 글에 이어 작성된 내용입니다. [TS] TypeScript 시작하기 TypeScript(TS)를 공부한 내용에 대해 기록합니다. 왜 TS를 공부하게 되었는지?

munak.tistory.com

 

유틸리티 타입에 대하여

Omit 타입 구현해보기

 


 

유틸리티 타입

타입스크립트에서는 기본적인 타입들과 함께 유틸리티 타입이 제공됩니다.

기본 타입은 기본 자료형과 사용자 정의 타입을 포함하며,

유틸리티 타입은 기존 타입을 변환하거나 조작하여 새로운 타입을 만들어내는 기능입니다. 

 

 

 

유틸리티 타입의 종류

유틸리티 타입은 많은 종류가 있으며, 주로 제네릭 타입과 함께 사용됩니다. 

각 유틸리티 타입은 특정한 작업에 최적화되어 있으며, 필요에 따라 다양한 상황에서 활용할 수 있습니다.

 

주로 쓰이는 타입은 `Partial, Record, Pick, Omit, Exclude` 정도입니다. 

 

  • Partial<T>:  T의 모든 속성을 선택적으로 만들어주는 타입. ⭐
  • Required<T>: T의 모든 속성을 필수로 만들어주는 타입.
  • Readonly<T>:  T의 모든 속성을 읽기 전용으로 만들어주는 타입. 
  • Record<K, T>: 키 K와 값 T를 갖는 타입을 생성. 보통 객체를 생성하는 데 사용됩니다.
  • Pick<T, K>: T에서 특정 속성들만 선택하여 새로운 타입을 만들어주는 타입.
  • Omit<T, K>: T에서 특정 속성을 제외한 새로운 타입을 만들어주는 타입.
  • Exclude<T, U>:  T에서 U에 할당 가능한 모든 속성을 제외한 타입.
  • Extract<T, U>: T에서 U에 할당 가능한 모든 속성을 추출한 타입.
  • NonNullable<T>: T에서 null과 undefined를 제외한 모든 속성을 가진 타입.
  • ReturnType<T>: 함수 타입 T의 반환 타입 추출.
  • Parameters<T>: 함수 타입 T의 매개변수 타입들을 추출
  • InstanceType<T>: 클래스 타입 T의 인스턴스 타입을 추출.
  • ThisParameterType<T>: 함수 타입 T에서 this 매개변수의 타입을 추출.
  • OmitThisParameter<T>: 함수 타입 T에서 this 매개변수를 제외한 새로운 함수 타입을 생성.
  • ThisType<T>: this 컨텍스트의 타입을 명시적으로 지정하는 타입.

 

 

 

Partial<T> 

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

 

Partial 유틸리티 타입은 `T`의 모든 속성을 선택적으로 만들어주는 타입입니다.

예를 들어 아래와 같이 인터페이스나 타입에 Partial을 적용할 수 있습니다.

 

interface Todo {
    id: number;
    title: string;
    description: string;
    completed: boolean;
}

type PartialTodo = Partial<Todo>;

const partialTodo: PartialTodo = {
    id: 1,
    title: 'TS study'
    // description, completed은 선택적으로 지정하지 않음.
};

 

위 예시에서는 Partial 유틸리티 타입을 사용하여 Todo 인터페이스를 `PartialTodo`로 변환했습니다.

`Todo` 타입의 모든 속성이 선택적으로 만들어지므로,

`partialTodo`객체는 id와 title을 제외한 나머지 속성을 필요에 따라 선택적으로 지정할 수 있습니다.

 

// 모든 속성이 optional 하게 바뀐다...!
type PartialTodo = {
    id?: number | undefined;
    title?: string | undefined;
    description?: string | undefined;
    completed?: boolean | undefined;
}

 

 

 

이어서 유틸리티 타입을 직접 구현해 보면서 좀 더 타입에 대해서 알아가 보겠습니다.

 


 

Pick<T> 구현해보기

Pick은 특정 속성들만 선택하여 새로운 타입을 만들어주는 유틸리티 타입입니다.

주어진 타입에서 원하는 속성들만 추출하여 새로운 타입을 만들 때 사용할 수 있죠.

 

Pick을 구현하는 건 생각보다 간단합니다. mapped type 이용하는 거죠.

type MyPick<T, K extends keyof T > = {
	[key in K] : T[key]
}


입력받은 키 집합인 K로부터 key를 하나씩 뽑아서 T[key], 원래의 타입으로 다시 매핑해주기만 하면 됩니다.

 

 

 

Omit<T, K> 구현해보기

Omit 유틸리티 타입은 T에서 특정 속성을 제외한 새로운 타입을 만들어주는 타입입니다.

예를 들어, 위 `Todo`인터페이스에서 description, completed 속성을 지운 새로운 타입을 만들고 싶다면 Omit을 사용하면 됩니다.

interface Todo {
    id: number;
    title: string;
    description: string;
    completed: boolean;
}

// Todo에서 id와 completed 속성을 제외한 새로운 타입이 생성된다.
type OmitTodo = Omit<Todo, 'id' | 'completed'>;

 

// 유니온으로 전달된 속성을 제외한 새로운 속성이 만들어졌다.
type OmitTodo = {
    title: string;
    description: string;
}

 

 

 

이번에는 `MyOmit<T, K>`을 만들어 볼까요?

MyOmit<T, K> = any

 

 

 

1. K는 T 속성에 종속된다.

type MyOmit<T, K extends keyof T> = any

 

`keyof T`는 T의 모든 속성(키)을 포함하는 유니온 타입을 생성하는 문장입니다. 

그리고 K가 T의 키에 제한되도록  타입제한자(extends)를 사용하였습니다.

 

따라서 K는 T의 키에 종속되는 값, T 키에 정의된 값 만 K에 입력할 수 있습니다.

 

 

 

2. mapped type

객체 T의 속성 유니온타입으로 가져오기 위해 mapped type을 사용합니다.

type MyOmit<T, K extends keyof T> = {
  [key in keyof T] : T[key]
}

 

`[key in keyof T]`는 T로부터 key를 모두 뽑아 유니온 타입을 만들고,

그 유니온 타입으로부터 하나씩 타입을 뽑아서 key에 매핑하여 순회할 거라는 의미가 됩니다. 

 

`[K in 유니온타입]`와 같이 대괄호에 `in`으로 이어진 구문들은 모두 이렇게 해석하시면 됩니다.

 

현재 MyOmit 타입은 T의 모든 키를 뽑아서 각 키의 원래 타입으로 다시 매핑하니, 원래 타입 그대로가 됩니다.

 

 

 

3. as...

type MyOmit<T, K extends keyof T> = {
  [key in keyof T as key extends K ? never : key] : T[key]
}

 

이제 K에 포함되는 key들을 제거하겠습니다.

추가된 부분은 `as key extends K ? never : key`인데요.

 

이 부분의 해석은 다음과 같습니다. `key가 만약 K에 포함된다면 never, K에 포함되지 않으면 원래 키 key를 그대로 써주세요!`

 

따라서 K에 포함된 키들은 never를 통해 제거되어 반환되지 않습니다. 

포함되지 않는 키들만 원래의 타입과 매핑되어 반환되게 되는 거죠.

 


그럼 기존의 Omit과 결과가 같은지 확인해 볼까요. (Playground 링크)

type MyOmit<T, K extends keyof T> = {
  [key in keyof T as key extends K ? never : key] : T[key]
}

interface Todo {
    id: number;
    title: string;
    description: string;
    completed: boolean;
}

type MyOmitTodo = MyOmit<Todo, 'id' | 'completed'>;
type OmitTodo = Omit<Todo, 'id' | 'completed'>;

 

결과가 같은 걸 확인해 보실 수 있을 겁니다.

 

다른 점이 딱 하나 있다면 Omit의 제너릭 파라미터 `K는 keyof T 가 아니라 any`입니다!
실제 Omit 타입에서는 실수로 제거할 키를 잘못 입력해도 컴파일 에러가 나지 않죠.

 

 


 

유틸리티 타입을 잘 이용하면 반복되는 코드를 줄이고, 타입 정의를 간소화하여 코드의 가독성을 높일 수 있습니다! ✌️

 

제가 소개한 내용 외에 중요한 내용이 많으니

추가적인 내용은 아래 공식 문서를 참고하시면 좋을 것 같습니다.

 

 

Documentation - Utility Types

Types which are globally included in TypeScript

www.typescriptlang.org

 

감사합니다. 

 

 

공부한 내용을 복습/기록하기 위해 작성한 글이므로 내용에 오류가 있을 수 있습니다.

댓글
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Total
Today
Yesterday