ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Typescript] Type Guard
    Frontend/Typescript 2023. 3. 12. 16:31

    Type Guard란?

    변수의 타입을 특정 영역으로 제한

    → 타입을 더 한정되게 좁혀 나가는 과정

    공식문서에 따르면, 타입가드는 특정 스코프 내에서 사용되는 타입을 런타임에 체크하는 일종의 표현입니다.

    Type Guard 사용해야 하는 상황

    Union type을 사용할 시 발생하는 오류

    유니온 타입을 사용해 코드를 작성하면 타입스크립트 컴파일러는 “타입체크”를 수행할 수 없다.

    //JS코드
    
    function add(a, b) {
        return a + b;
    }
    
    // 실행결과가 예상과 다를 순 있어도, 컴파일도중에 오류가 발생하진 않는다.
    
    //TS코드
    
    function add(a: number | string, b: number | string): number | string {
            return a + b;
    }
    
    //코드 작성중 TS 컴파일러가 오류를 체크해준다
    

    → 타입스크립트가 알려주는 오류.

    이렇게 “타입체크” 문제를 해결하기 위해 유니온 타입으로 표현된 요소가 갖고있는 타입을 검사하는 코드를 따로 삽입해야 한다.

    Type Guard 구현하기

    - 공식 문서에서 소개하고 있는 방법

    function isFish(pet: Fish | Bird): pet is Fish {
      return (pet as Fish).swim !== undefined;
      // 
    }
    
    const pet = getSmallPet();
     
    if (isFish(pet)) {
      pet.swim();
    } else {
      pet.fly();
    }
    

    + 그 외 방법들

    - ‘typeof’ 연산자 유니온 타입이 string, number 등 기본 타입으로만 구성되어 있을 경우에 사용한다.

    function add(a: number | string, b: number | string): number | string {
       
       //타입가드: a와 b 가 number일 경우
       
       if (typeof a === 'number' && typeof b === 'number') {
            return a + b}
       
       //타입가드 a와 b가 string일 경우
       
       else if (typeof a === 'string' && typeof b === 'string') {
            return a + b 
        }
    
        else {return 'a and b should have same type'}
    }
    
    //위의 코드의 두개의 if문이 type guard 역할을 수행한다.
    

    - 타입가드가 필요없는 예시

    type CheeseBurger = {
        numBread: number;
        numCheese: number;
    }
    
    type BeefBurger = {
        numBread: number;
        numBeef: number;
    }
    
    // 재료를 알려주는 함수
    // parameter가 union type으로 되어있다.
    
    function sayIngredient (burger: CheeseBurger | BeefBurger) {
        console.log('number of bread : ' + burger.numBread)
        }
        
    
     //numBread는 CheeseBurger와 BeefBurger 전부 가지고있는 요소이다.
     // 그래서 TS 컴파일러가 이미 알고있기때문에
     // Type Guard가 필요없다.
    

    - ‘typeof’는 내가 정의한 커스텀 타입에는 사용할 수 없다.

    // burger매개변수는 유니온타입이 지정되어있다.
    
    function sayIngredient (burger: CheeseBurger | BeefBurger) {
    
    //BeefBurger일때만 실행되는 코드.
    // 커스텀타입은 typeof로 체크해줄수 없다.
    
    if (typeof burger === 'BeefBurger') {
            console.log('number of beef : ' + burger.numBeef
        }
    
    console.log('number of bread : ' + burger.numBread)
        }
    // 아래와 같은 오류 메시지 출력
    

    typeof의 반환값은 기본 내장 타입으로 구성된다.

    - 커스텀 타입 타입가드 성분을 추적하는 ‘in’ 활용하기.

    function sayIngredient (burger: CheeseBurger | BeefBurger) {
        console.log('number of bread : ' + burger.numBread)
        
        //타입가드 numCheese와 numBeef요소의 존재로
        // CheeseBurger인지 BeefBurger인지 검사한다.
        
        if('numCheese' in burger) {
            console.log('number of cheese : ' +  burger.numCheese)
        } else if ('numBeef' in burger) {
            console.log('number of beef : ' + burger.numBeef)
            }
        
    }
    

    - 클래스 유니온타입 타입가드 ’instanceof’ 활용하기.

    class Burger {
        public setMenu = 'french fries and coke'
    
    }
    
    class SideMenu {
        public beverage = 'coke or cider'
    }
    
    const order1 = new Burger()
    const order2 = new SideMenu()
    
    //burger와 sideMenu의 유니온타입을 
    //매개변수의 타입으로 지정한 함수
    
    function checkPlusOrder (order: Burger | SideMenu) {
        if (order instanceof Burger){
            console.log(order.setMenu);
        }
        if (order instanceof SideMenu){
            console.log(order.beverage);
        }
    }
    //instanceof를 이용하여 order가 어떤 class에 속하는지 체크한다.
    

    - discriminated union 사용하기 유니온 타입에 들어가는 “구분대상”의 개별요소에 구분을 위한 성분을 집어넣고, switch문을 타입가드로 이용한다.

    // interface에 kind라는 구분용 요소를 넣어놓았다.
    
    interface Burger {
        kind: "burger";
        setMenu: string;
    }
    
    interface SideMenu {
        kind: "sideMenu";
        beverage: string;
    }
    
    function checkPlusOrder(order: Burger | SideMenu) {
        switch (order.kind) {
            case "burger":
                console.log(order.setMenu);
                break;
            case "sideMenu":
                console.log(order.beverage);
                break;
        }
    }
    //switch문과 기존의 interface에 심어놓은 구분용변수  "kind"
    //를 이용하여 type guard를 구현하였다.
    

    - enum 타입 타입가드

    enum Color {
      Red = "red",
      Blue = "blue",
    }
    
    enum Size {
      Big = "big",
      Small = "small",
    }
    
    const isColorEnum = (something: Color | Size): something is Color =>
      (something as Color).Red !== undefined; // Error: Property 'Red' does not exist on type 'Color'.
    

    넘어올 수 있는 타입인 Color와 Size는 모두 enum이기 때문에, something은 red, blue, big, small 중 하나다. 따라서 애초에 something에는 Red 라는 프로퍼티가 없다.

    다음과 같이 작성하면 정상 동작한다.

    enum Color {
      Red = "red",
      Blue = "blue",
    }
    
    enum Size {
      Big = "big",
      Small = "small",
    }
    
    const isColorEnum = (something: Color | Size): something is Color =>
      Object.values(Color).includes(something as Color);
    
    console.log(isColorEnum(Color.Red)); // true
    console.log(isColorEnum(Size.Small)); // false
    

    - falsy value(null 등)을 걸러낼 때

    type Car = {
      color: string,
      price: number,
    };
    
    type Person = {
      name: string,
      age: number,
      height: number,
    };
    
    function logSomething(something: Car | Person | null) {
      if (something) {
        // something: Car | Person
      } else {
        // something: null;
      }
    }
    

    'Frontend > Typescript' 카테고리의 다른 글

    [Typescript] Enum  (0) 2023.03.12
    [Typescript] type과 Interface  (0) 2023.03.12
    [TypeScript] 잘 사용하기  (0) 2023.02.19
    [TypeScript] 동작 원리, 장점과 단점  (0) 2023.02.19

    댓글

Designed by Tistory.