-
[Typescript] Type GuardFrontend/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