티스토리 뷰

728x90
반응형

타입스크립트는 타입을 통해 다형성을 타입 안전하게 다룰수 있습니다

 

공식홈페이지에 있는 예를 가져와보았습니다

 

type Fish = { swim: () => void };
type Bird = { fly: () => void };
 
function move(animal: Fish | Bird) {
  if ("swim" in animal) {
    return animal.swim();
  }
 
  return animal.fly();
}

 

swim 을 가지고 있는 Fish 타입과

fly 를 가지고 있는 Bird 타입이 있습니다

 

그리고 Fish와 Bird 타입을 인자로 받는 move 함수가 있습니다

 

Fish 타입의 인자가 왔을때는 swim을 호출해야하고

Bird 타입의 인자가 왔을때는 fly를 호출해야합니다

 

 

이 경우 어떻게 타입 안전하게 각각의 함수를 호출할수 있을까요?

 

in 오퍼레이터를 통해서 프로퍼티가 있는지 확인을 통해 안전하게 각 타입에 맞는 함수를 호출할수 있습니다

 

 

 

다음은 또 다른 방법으로 타입안전하게 다형성을 다루어볼께요

 

Shape 라는 유니온 타입은 Circle과 Square 타입을 포함합니다

 

interface Circle {
  kind: "circle";
  radius: number;
}
 
interface Square {
  kind: "square";
  sideLength: number;
}
 
type Shape = Circle | Square;

 

각 도형의 면적을 구하는 getArea 라는 함수를 만들었습니다

 

Shape 타입을 인자로 받습니다

 

function getArea(shape: Shape) {
  switch (shape.kind) {
    case "circle":
      return Math.PI * shape.radius ** 2;
    case "square":
      return shape.sideLength ** 2;
  }
}

 

 

그런데 각 도형마다 면적을 구하는 방법이 다릅니다

 

원은 반지름이 필요하고, 정사각형을 한쪽변의 길이가 필요합니다

 

switch를 통해 kind 라는 속성을 조회하여

 

타입안전하게 코드를 작성할수 있습니다

 

interface Triangle {
  kind: "triangle";
  sideLength: number;
}
 
type Shape = Circle | Square | Triangle;
 
function getArea(shape: Shape) {
  switch (shape.kind) {
    case "circle":
      return Math.PI * shape.radius ** 2;
    case "square":
      return shape.sideLength ** 2;
    default:
      const _exhaustiveCheck: never = shape;
      return _exhaustiveCheck;
  }
}

 

 

다형성을 이용하여 함수를 작성할때 한가지 알아두면 더 좋은 never 타입이 있습니다

 

위의 예제에서 Triangle 이라는 새로운 도형의 타입을 추가했고

 

Shape가 해당 타입을 갖도록 하였습니다

 

만약 타입은 추가하였는데

 

getArea 함수에 해당 도형에 대한 대응하는 코드를 넣지 않는다면 

 

우리는 예상치못한 런타임에러를 발견하게 될거예요

 

 

그래서 바로 이때 필요한게 never 타입입니다

 

never 타입을 선언하여 모든 타입을 다 다루었다라는 타입 안정성을 확보하게 됩니다

 

 

위의 예제에서는 Triangle 타입을 다루지 않았으므로

 

Type 'Triangle' is not assignable to type 'never'.

 

never 타입이 선언된 구문에 이와 같은 오류를 발견할수 있어 타입안전하게 코드를 작성할수 있는것을 보장합니다

 

 

이러한 예를 실사용의 예제로 확대해볼까요

 

 

type NetworkLoadingState = {
  state: "loading";
};
type NetworkFailedState = {
  state: "failed";
  code: number;
};
type NetworkSuccessState = {
  state: "success";
  response: {
    title: string;
    duration: number;
    summary: string;
  };
};
// Create a type which represents only one of the above types
// but you aren't sure which it is yet.
type NetworkState =
  | NetworkLoadingState
  | NetworkFailedState
  | NetworkSuccessState;

 

 

다양한 상태(로딩, 실패, 성공)를 가지고 있는 NetworkState 타입이 있고

 

각 타입별로 각각의 타입에 필요한 속성들을 가지고 있을때

 

우리는 위에 배웠던 것들을 통해서 타입안전한 코드를 작성할수 있습니다

 

 

type NetworkFromCachedState = {
  state: "from_cache";
  id: string;
  response: NetworkSuccessState["response"];
};
 
type NetworkState =
  | NetworkLoadingState
  | NetworkFailedState
  | NetworkSuccessState
  | NetworkFromCachedState;
 
function logger(s: NetworkState) {
  switch (s.state) {
    case "loading":
      return "loading request";
    case "failed":
      return `failed with code ${s.code}`;
    case "success":
      return "got response";
  }
}

 

 

이렇게 새로운 타입도 얼마든지 추가할수 있고, never 타입을 이용한다면 더 안전한 코드를 작성할수 있을거예요

 

 

728x90
반응형
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/10   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
글 보관함