Posts [TypeScript] 타입스크립트 Utility types (1) - Exclude, Omit
Post
Cancel

[TypeScript] 타입스크립트 Utility types (1) - Exclude, Omit

타입스크립트 Utility types (1) - Exclude, Omit


타입스크립트에서는 type transformation을 유연하게 도와 주는 여러 유틸리티 타입을 제공하고 있습니다. (타입스크립트 유틸리티 타입)

그 중에서 실무에서도 유용하게 쓰이는 것들에 대해 포스팅해 보려고 합니다.

이번에는 어떤 것을 제외한다는 성격은 유사하지만, 쓰임새가 다소 다른 두 타입인 ExcludeOmit 에 대해 먼저 알아보겠습니다.


Exclude<T, U>

Exclude from T those types that are assignable to U. (typescript 2.8 릴리즈)

타입스크립트에서는 조건부 형식으로 타입을 정의할 수 있는데요, 아래와 같은 형태를 취합니다.

1
T extends U ? X : Y

조건식 결과에 따라 X가 될 수도, Y가 될 수도 있습니다.

타입스크립트는 용도에 맞게 조건부 타입을 활용한 새로운 타입들을 미리 정의해 두었고, 이것을 Predefined conditional types 라고 하며 그 중 하나가 Exclude 입니다.

실제 Exclude는 아래와 같이 정의되어 있습니다.

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

즉, T에 오는 타입들 중 U에 오는 것들은 제외하겠다는 의미입니다.

1
2
3
4
type Fruit = "cherry" | "banana" | "strawberry" | "lemon";

type RedFruit = Exclude<Fruit, "banana" | "lemon">;
// type RedFruit = "cherry" | "strawberry" 와 같다.

위에서 RedFruit 타입은 Fruit 중에서 ‘banana’, ‘lemon’ 타입을 제외한 것으로 정의됩니다.

따라서 아래와 같은 예시를 확인할 수 있습니다.

1
2
const fruit: RedFruit = "strawberry"; // OK
const fruit2: RedFruit = "banana"; // ERROR, ts(2322)_Type '"banana"' is not assignable to type '"cherry" | "strawberry"'.

조금 더 타입을 간단히 해서 추가로 살펴보자면 다음과 같습니다.

1
2
type Todo = Exclude<"a" | "b" | "c", "b" | "c">;
// type Todo = "a" 와 같다.

쉽게 말해 "a" | "b" | "c" 에서 "b" | "c"를 제외하였으니, “a” 타입만 남는 것입니다.

그래서 Exclude는 어떤 유니온 타입에서 특정한 타입들을 제외하려고 할 때 보통 사용하게 됩니다.

가령 이런 상황에서도 사용할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
interface Player {
  flyingPet: string;
  matchRank: string;
  matchWin: string;
  matchTime: string;
  matchRetired: string;
  rankinggrade2: string;
}

type Match = Exclude<keyof Player, "flyingPet">;
// type Match = "matchRank" | "matchWin" | "matchTime" | "matchRetired" | "rankinggrade2" 와 같다.

코드를 작성하다 보면, 어떤 객체의 키값만을 모아서 새로운 타입으로 정의해야 하는 상황이 올 수 있습니다.

이 때 위처럼 keyof Player를 통해 Player의 key 값만을 모아 새로운 타입을 정의할 수 있는데, 그 중에서도 몇 가지는 제외하고 싶을 경우 Exclude를 사용하면 됩니다.

(keyof 연산자는 쉽게 말해 어떤 객체 유형의 타입에서 key 값만을 골라 타입으로 만들어 주는 역할을 합니다.)


Omit<Type, Keys>

TypeScript 3.5 introduces the new Omit helper type, which creates a new type with some properties dropped from the original. (typescript 3.5 릴리즈)

Omit 은 어떤 정의된 객체 형태의 타입에서 특정한 프로퍼티들을 제외시켜 줍니다.

1
2
3
4
5
6
7
8
interface Match {
  accountNo: number;
  matchRank: number;
  name: string;
  age: number;
}

type PersonInfo = Omit<Match, "accountNo" | "matchRank">;

위에서 PersonInfo 타입은 Match 타입의 프로퍼티들 중 ‘accountNo’, ‘matchRank’를 제외한 나머지(name, age)로 이루어지게 됩니다.

즉, PersonInfo 타입은 아래와 같이 name, age 프로퍼티를 가진 객체로 정의할 수 있습니다.

1
2
3
4
const person: PersonInfo = {
  name: "John",
  age: 18,
};

만약 아래와 같이 제외시켰던 matchRank를 넣으려 하면, 에러가 발생합니다.

1
2
3
4
5
6
7
8
const person: PersonInfo = {
  name: "John",
  age: 18,
  matchRank: "start",
  /* ts(2322) : Type '{ name: string; age: number; matchRank: string; }' is not assignable to type 'Omit<Match, "accountNo" | "matchRank">'.

  Object literal may only specify known properties, and 'matchRank' does not exist in type 'Omit<Match, "accountNo" | "matchRank">'. */
};

PersonInfo 타입은 Match 타입에서 ‘accountNo’, ‘matchRank’ 프로퍼티를 제외시킨 새로운 타입이기 때문에, 당연히 matchRank를 넣으려 하면 오류가 발생하는 것입니다.

이처럼 Omit은 기존에 interface 등으로 정의한 타입에서, 특정한 프로퍼티들만 제외하고 재활용하고 싶을 때 유용하게 사용할 수 있습니다.


간단하게 두 타입의 차이점을 정리하면

  • Omit : 정의된 객체 유형의 타입(예: interface)에서 특정 프로퍼티를 제외한 타입을 정의하고 싶을 때

  • Exclude : 어떤 타입(보통 유니온)에서 특정한 타입들을 제외시켜 정의하고 싶을 때

사용하게 됩니다.

참고로, 사실 Omit의 경우 정의할 때 Exclude도 활용하고 있습니다.

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

이처럼, 타입들을 사용하고 정의함에 있어서 좀 더 유연하게 도움을 주는 역할임을 분명하게 확인할 수 있습니다.

(위의 Pick 도 같은 유틸리티 타입인데 추후 다른 것들과 함께 포스팅으로 다룰 예정입니다.)


레퍼런스

[TypeScript] 타입스크립트 Type Guard 및 타입을 좁혀나가는 여러가지 방법

-