타입스크립트 Utility types (1) - Exclude, Omit, Pick
타입스크립트에서는 type transformation을 유연하게 도와 주는 여러 유틸리티 타입을 제공하고 있습니다. (타입스크립트 유틸리티 타입)
그 중에서 실무에서도 유용하게 쓰이는 것들에 대해 포스팅해 보려고 합니다.
이번에는 어떤 것을 제외한다는 성격은 유사하지만, 쓰임새가 다소 다른 두 타입인 Exclude
과 Omit
을 알아보고, 추가로 Pick
에 대해서도 알아보겠습니다.
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 등으로 정의한 타입에서, 특정한 프로퍼티들만 제외하고 재활용하고 싶을 때 유용하게 사용할 수 있습니다.
Pick<Type, Keys>
Constructs a type by picking the set of properties Keys (string literal or union of string literals) from Type. (typescript 2.1 릴리즈)
Pick 은 어떤 정의된 객체 형태의 타입에서 특정한 프로퍼티들만 골라서 새로운 타입으로 만듭니다.
1
2
3
4
5
6
7
8
9
10
11
12
interface Todo {
title: string;
description: string;
completed: boolean;
}
type TodoPreview = Pick<Todo, "title" | "completed">;
const todo: TodoPreview = {
title: "Clean room",
completed: false,
};
Todo
타입은 title, description, completed를 프로퍼티로 가집니다.
그리고 TodoPreview
타입은 Todo
타입의 프로퍼티들 중에서 title, completed 만 골라낸 타입이 됩니다.
간단하게 오늘 알아본 유틸리티 타입들을 정리하면
Omit
: 정의된 객체 유형의 타입에서 특정 프로퍼티를 제외한 타입을 정의하고 싶을 때Exclude
: 어떤 타입(보통 유니온)에서 특정한 타입들을 제외시켜 정의하고 싶을 때Pick
: 정의된 객체 유형의 타입에서 특정 프로퍼티를 선택한 새로운 타입을 정의하고 싶을 때
사용하게 됩니다.
참고로, 사실 Omit
의 경우 정의할 때 Exclude
도 활용하고 있습니다.
1
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
이처럼, 타입들을 사용하고 정의함에 있어서 좀 더 유연하게 도움을 주는 역할임을 분명하게 확인할 수 있습니다.
레퍼런스