Typescript union & interection type 정리

Derek·2021년 3월 22일
0

typescript_study

목록 보기
6/6
post-thumbnail

안녕하세요, Derek 입니다. :)

이번 게시물에서는 typescriptadvanced typeunion 타입과 intersection 타입에 대해서 간단히 정리해보려고 합니다.

레츠기릿.


1. Union type

1) Definition

typescriptunion type 은 다음과 같이 정의한다.

TypeScript allows us to use more than one data type for a variable or a function parameter. This is called union type.

2) How to Use? - 1

그렇다면 이 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 이다.

3) Why union type?

왜? union type 을 쓰는가? 에 대해서 정리해보려 합니다.

굳이 왜 쓰는지, 그냥 함수 선언할때 타입 정확히 딱! 하면 될거 왜 이렇게 하느냐, 라고 생각이 들더군요.

그 이유는 두 개의 함수를 비교하며 설명하겠습니다.

union type 예시 코드 1

function 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, but TypeScript will be okay with it.

string 이나 number 가 아닌 변수가 오게되면, compile time 에는 오류를 잡지 못하게 되어, run time 에 에러가 나오게 됩니다.

위 그림과 같이 마지막 줄에서 true 를 인자로 두어도, padding 의 타입은 any 이기 때문에 에러를 감지하지 못합니다.

결과는 위와 같이 나옵니다.


이를 union type 을 사용해서 사전에 미리 방지해보겠습니다.

union type 예시 코드 2

function 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"

위와 같이 paddingstring | number 이라는 union type 을 명시하였습니다.

즉, paddingstring 이거나 number 이라는 뜻이죠.

이렇게 한다면 아래와 같이 에러를 감지합니다.

14번째 줄에 true 아래 에러를 감지함을 알 수 있습니다.

14번째 줄에서 넘겨준 true 값은 not assignable to string | number 라고 합니다.

이렇게 미리 에러를 방지 할 수 있겠죠.

4) How to Use? - 2

위와 같이 기본 자료형으로 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 이다.

둘 중에 어떤 변수가 올지 모르니까 둘 다 가지고 있는 속성만 접근 가능하다! (?)


2. Intersection type

1) Definition

typescriptintersection 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 과 달리 모든 속성에 접근 가능합니다.

2) How to use?

어떻게 쓰는지, 예시 코드는 다음과 같다.

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.

(핵심 부분에 강조표시를 했다. 중요부분이니까 영어라고 대충읽지 말자.)

3) Example

공식 문서에 기록되어 있는 예시를 가져와봤다.

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하도록 설계했다고 한다.


이렇게 자세하게 정리해보았다.

갈길이 멀군.

춥다. 감기조심하자. 빠셍!

틀린 부분이 있다면 댓글 부탁드립니다.

감사합니다. 😀

profile
Whereof one cannot speak, thereof one must be silent.

0개의 댓글