intro타입스크립트는 좋은 언어이다.
특히, 자바스크립트로 개발을 하던 사람이 타입스크립트를 만나면 더욱 위 말에 공감할 것이다.
이유는 선명하다.
"정적 타입 지원"
우리가 완벽한 천재가 아니라면, 모든 변수, 함수의 인풋, 아웃풋 컴포넌트의 props 를 다 외우고 있지 못할 것이다.
타입스크립트는 위의 모든 코드를 정적 타입화 하고 이는 개발자에게 많은 이점을 안겨준다. (오류 방지, 리팩터링 용이, 코드 리뷰 가독성, ide로 부터 얻는 자동완성 서비스로 인한 생산성 향상 등)
그래서 나는 1년 반 넘게 매일 Microsoft 를 찬양하는 중이다. 좋은 자바스크립트의 상위 집합을 만들어줘서 감사하다고. 근데 주식이 떨어지지 않아 사질 못 하고 있다...
서론이 길었다.
요약하자면, 나는 타입스크립트를 너무 잘 사용하고 있다. 또한 그 집합에서도 더 효율적이고 더 안전한 코딩을 추구하는 사람이다.
그런데 모든 것에는 약점이 있고, 완벽한 어떤 것은 없다고 생각한다.
타입스크립트도 마찬가지다.
최근 느낀 점은, Utility Types 에서 Omit<T, K> 타입에 대한 이야기다. ( Object.keys() 에 대한 불만도 있지만, 나중에 다뤄볼까한다.)
유틸리티 타입은 말 그대로 부수적인 타입이며, 일반적인 타입은 아니지만 일반적인 타입을 편하게 사용하도록 도와주는 타입이다.
대표적으로는 Partial, Required, Record, Pick, Exclude, Extract, ReturnType, NonNullable, 그리고 Omit 이런 타입들이 있다.
이런 타입들 모두 혼자서는 쓰이지 않고, 일반적인 타입들과 결합해서 쓴다.
Pick, Omit 비교Omit 에 대한 이야기를 하기 전에, Omit과 Pick 의 사용 방법을 간단히 예제로 소개해보려한다.
type Todo = {
title: string;
description: string;
completed: boolean;
createdAt: number;
};
위의 Todo 타입을 사용하여 새로운 타입을 만들어 보겠다. 먼저 Pick.
Pick (2.1 Released)Pick<Type, Keys> 은 객체 타입에서 원하는 키들만 뽑아서 새로운 타입을 만들어준다. 아래는 Todo 라는 타입에서 title, completed라는 속성만 뽑아서 새로운 객체 타입인 TodoPreviewPick 을 생성하는 사용 예제이다.
type TodoPreviewPick = Pick<Todo, "title" | "completed">;
/*
{
title: string;
completed: boolean;
}
*/
Omit (3.5 Released)이름에서도 유추 가능하듯이, Omit<Type, keys> 은 Pick 의 반대 유틸리티 타입이다. 위의 Pick의 예제와 똑같은 타입을 Omit 으로 만드는 예제가 아래에 있다.
type TodoPreviewOmit = Omit<Todo, "description" | "createdAt">;
/*
{
title: string;
completed: boolean;
}
*/
둘다 결과는 같다. title, completed 속성만 남고, description, createdAt 속성은 제거된 타입이다.
위의 두 유틸리티 타입을 필요에 따라 적절히 사용하면 아무 문제가 없다.
예를 들어, 기존 객체의 key, value 가 10개 이고, 내가 필요한 key, value가 2개라면 당연히 Pick을 쓰는게 편하다. 기존 타입에 필요한 key를 2개 만 적어주면 되니까.
반대로, 기존 객체의 key가 10개, 필요한 타입이 8개라면, Omit을 쓰는게 편하다. 기존 타입에 필요한 타입을 8개 작성해주는 것 보다, 필요없는 key 2개를 적어주면 되니까.
Omit 타입에 대한 의문점type intelisense by vscode)먼저, Pick 타입을 작성할 때, 타입 추론이다. 원본 타입을 넘겨준 후, 필요한 key값을 추천해준다. 아주 편리하다.

그렇다면, Omit 타입을 사용할 때도, 제거할 타입을 추천해주는 것이 당연하지 않나? 라고 생각했다.
그러나 그렇지 않았다. 내가 원하는 description, createdAt 타입을 추천해주지 않았다.

** 그런데 이 문제는 다른 에디터인 webstorm 에서는 발생하지 않는다. ide가 자체적으로 타입추론을 하는 느낌이다.
유틸리티 타입으로 새로운 타입으로 확장할 때, 원본 타입을 기반으로 변화시키므로 에러를 컨트롤 해주는 것이 중요하다고 생각하는데, Omit 은 그렇지 않다.
먼저, Pick 은 잘된다. 우리는 description 을 description"s" 로 오타를 낼 수도 있다. 사람이다 보니. 역시 Pick 은 에러를 잘 뱉어준다. (ts2344)

Omit 은 그렇지 않다. 오타를 내어도 에러를 안내하지 않는다. 이렇게 되면, 우리가 원하지 않는 description 타입이 고스란히 TodoPreviewOmit 타입에 들어 있게 된다.

Pick 과 Omit 은 반대되는 역할을 해주고 상당히 비슷한 생김새이다. 그러나 왜 동작에는 이토록 차이가 있을까 ? Omit도 Pick 처럼 타입 추론과, 에러 캐치를 잘 해주면 안되는 걸까 ??
이유는 선언된 타입의 구조가 다르기 때문이다.
// v4.6
/**
* From T, pick a set of properties whose keys are in the union K
*/
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
/**
* Construct a type with the properties of T except for those in type K.
*/
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
이렇게 타입스크립트의 두 유틸리티 타입은 선언되어있다.
차이점은 Pick은 K를 keyof T 로 한정지었고, Omit 은 K 가 keyof any 이다.
이럴 수가, any 타입이여서, 타입 추론을 못하고, 에러를 뱉어주지 못했던 거구나..
왜 이렇게 타입을 만들어 둔거지 ??
typescript 깃허브 이슈를 좀 찾아봤다.
질문
대충봐도 엄격한 Omit 타입을 만들어달라는 이야기 같다. 엄격하다면 타입 추론과 에러를 잘 보여줄테니.
답변
타입스크립트 개발자 중 한명인 라이언 카바노의 답변이다.
내용은, 더 엄격한 Omit 타입을 제공해야하는 것이 바람직하다고 생각하지만, 엄격한 버전을 제공한다는 것은 매우 전염성이 강하며, 사람들이 현명하게 선택할지가 불분명하다.
그러니 여러분들 필요에 따라 엄격한Omit인OmtiSrirct과 같은 타입을 만들어 쓰셔라.
오.. 공식적으로 이를 수정하거나, 새로운 타입을 추가해줄 수 없다고 말한 것이 아닌가라는 생각이 든다.
완전히 이해가 되지는 않지만, 그렇다면 그렇다는 거지.
type OmitStrict<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
필요한 개발자들은 위 같은 타입을 만들어 쓰면 될 것 같다.
잘 생각보니, 결국 각 유틸리티 타입의 목적의 차이가 이러한 차이를 만든 건가 ? 라는 결론에 도달했다.
Pick 은 사용 목적이 필요한 속성을 뽑는다 라는 것이면, 이는 뽑을 수 있는 속성의 집합이 제한적이다. 원본 타입에 있는 Key 들로 한정되어 있다.
그러나 Omit 은 사용 목적이 필요없는 속성을 제외한다 라는 것이면, 제외할 수있는 속성의 집합은 모든 key 가 될 수도 있겠다는 생각을 했다.
이 생각은 개인적인 입장이고, 억지로 끼워맞춘 설득의 근거일 수도 있다.
그래도 이런 흥미로운 이슈가 있고, 각자 좋은 방식대로 선택하여 개발을 하면 되지 않을까라는 생각을 해본다.