이번 게시물에서는 typescript
의 advanced type
인 union
타입과 intersection
타입에 대해서 간단히 정리해보려고 합니다.
레츠기릿.
Union type
typescript
의 union type
은 다음과 같이 정의한다.
TypeScript
allows us to use more than one data type for a variable or a function parameter. This is calledunion type
.
그렇다면 이 union type
을 어떻게 쓸까?
간단하다.
function logMessage(value: string | number) {
if(typeof value === "number") {
value.toLocaleString();
}
else {
value.toString();
}
throw new TypeError("value must be string or number");
}
logMessage
함수의 인자로 value
를 넘겨주는데, 그 타입이 union type
이다.
union type
?왜? union type
을 쓰는가? 에 대해서 정리해보려 합니다.
굳이 왜 쓰는지, 그냥 함수 선언할때 타입 정확히 딱! 하면 될거 왜 이렇게 하느냐, 라고 생각이 들더군요.
그 이유는 두 개의 함수를 비교하며 설명하겠습니다.
union type
예시 코드 1function padLeft(value: string, padding: any) { if (typeof padding === "number") { return Array(padding + 1).join(" ") + value; } if (typeof padding === "string") { return padding + value; } throw new Error(`Expected string or number, got '${typeof padding}'.`); } padLeft("Hello world", 4); // returns " Hello world"
여기서 padding
변수는 타입이 any
로 명시되어 있습니다.
즉, 아무 변수나 와도 typescript
가 okay를 해준다는 거죠.
공식 문서에는 다음과 같이 설명합니다.
The problem with
padLeft
in the above example is that its padding parameter is typed as any. That means that we can call it with an argument that’s neither a number nor a string, butTypeScript
will be okay with it.
string
이나 number
가 아닌 변수가 오게되면, compile time
에는 오류를 잡지 못하게 되어, run time
에 에러가 나오게 됩니다.
위 그림과 같이 마지막 줄에서 true
를 인자로 두어도, padding
의 타입은 any
이기 때문에 에러를 감지하지 못합니다.
결과는 위와 같이 나옵니다.
이를 union type
을 사용해서 사전에 미리 방지해보겠습니다.
union type
예시 코드 2function padLeft(value: string, padding: string | number) { if (typeof padding === "number") { return Array(padding + 1).join(" ") + value; } if (typeof padding === "string") { return padding + value; } throw new Error(`Expected string or number, got '${typeof padding}'.`); } padLeft("Hello world", 4); // returns " Hello world"
위와 같이 padding
에 string | number
이라는 union type
을 명시하였습니다.
즉, padding
은 string
이거나 number
이라는 뜻이죠.
이렇게 한다면 아래와 같이 에러를 감지합니다.
14번째 줄에 true
아래 에러를 감지함을 알 수 있습니다.
14번째 줄에서 넘겨준 true
값은 not assignable to string | number
라고 합니다.
이렇게 미리 에러를 방지 할 수 있겠죠.
위와 같이 기본 자료형으로 union type
을 쓸 수도 있지만, 아래와 같이 interface
와 쓸 수 있다.
interface DeveloperOperator {
name : string,
skill: string
}
interface WorkerOperator {
name : string,
age: number
}
function askSomeone(someone: WorkerOperator | DeveloperOperator) {
// Developer 와 Person의 공통된 속성인 name만 접근가능함.
console.log(someone.name);
}
askSomeone({name: "Derek", skill: "d"});
askSomeone({name: "Derek", age: 2});
위 askSomeone
함수에 쓰이는 someone
인자는 WorkerOperator | DeveloperOperator
이다.
WorkerOperator
혹은DeveloperOperator
이므로, 그 두 가지의 공통 속성에만 접근 가능하다.
얼핏 보면 두 가지 WorkerOperator, DeveloperOperator의 모든 속성에 접근 가능 할 것 처럼 보인다. (union
속성이 or이니까.. 그렇게 보이더라. 나만그런가?)
someone
변수는WorkerOperator | DeveloperOperator
이다.둘 중에 어떤 변수가 올지 모르니까 둘 다 가지고 있는 속성만 접근 가능하다! (?)
Intersection type
typescript
의 intersection type
은 다음과 같이 정의한다.
An
intersection type
creates a new type by combining multiple existing types.The new type has all features of the existing types.
union type
과 달리 모든 속성에 접근 가능합니다.
어떻게 쓰는지, 예시 코드는 다음과 같다.
function askSomeone(someone: DeveloperOperator & WorkerOperator) {
// Developer 와 Person의 모든 속성에 접근 가능함.
// Developer 와 Person 의 모든 속성을 합친 개념.
}
askSomeone({name: "Derek", skill: "webDevelop", age: 30})
앞서 언급한 union type
과는 사뭇 다르다.
askSomeone
함수에는 DeveloperOperator & WorkerOperator
인 타입인 someone
이 쓰인다.
즉, DeveloperOperator
속성과 WorkerOperator
속성에 대한 모든 인자에 접근 가능한 타입이라고 봐도 무방하다. :)
공식문서에는 이렇게 기술되어 있다.
Intersection types
are closely related to union types, but they are used very differently. An intersection type combines multiple types into one. This allows you to add together existing types to get a single type that has all the features you need. For example,Person & Serializable & Loggable
is a type which is all of Person and Serializable and Loggable. That means an object of this type will have all members of all three types.
(핵심 부분에 강조표시를 했다. 중요부분이니까 영어라고 대충읽지 말자.)
공식 문서에 기록되어 있는 예시를 가져와봤다.
interface ErrorHandling {
success: boolean;
error?: { message: string };
}
interface ArtworksData {
artworks: { title: string }[];
}
interface ArtistsData {
artists: { name: string }[];
}
// These interfaces are composed to have
// consistent error handling, and their own data.
type ArtworksResponse = ArtworksData & ErrorHandling;
type ArtistsResponse = ArtistsData & ErrorHandling;
const handleArtistsResponse = (response: ArtistsResponse) => {
if (response.error) {
console.error(response.error.message);
return;
}
console.log(response.artists);
};
handleArtistsResponse
함수에 사용되는 인자인 ArtistsResponse
의 타입은 ArtistsData & ErrorHandling
이다. 그렇다면 아래 두 가지 타입에 모두 접근 가능하다.
interface ErrorHandling {
success: boolean;
error?: { message: string };
}
interface ArtistsData {
artists: { name: string }[];
}
이는 공식문서에서 이렇게 밝히더라.
If you had networking requests with consistent error handling then you could separate out the error handling into its own type which is merged with types which correspond to a single response type.
error handling
을 따로 빼서 merge하도록 설계했다고 한다.
이렇게 자세하게 정리해보았다.
갈길이 멀군.
춥다. 감기조심하자. 빠셍!
틀린 부분이 있다면 댓글 부탁드립니다.
감사합니다. 😀