본문 바로가기
TypeScript

0046. TypeScript 공부하기6 - 타입 좁히기, 서로소 유니온 타입

by 보초코더^_^;; 2024. 1. 23.
반응형

섹션 3. 타입스크립트 이해하기

7강. 타입 좁히기

1) 타입 좁히기

조건문 등을 이용해 넓은 타입에서 좁은 타입으로 타입을 상황에 따라 좁히는 방법을 말한다.

 

value가 number면 toFixed,

value가 string이면 toUpperCase를 쓰도록 한다.

value의 타입이 number | string이지만 둘 다 사용이 가능하지는 않다.

둘 중 하나로 명확하게 표현을 해줘야 한다.

 

// value => number : toFixed
// value => string : toUpperCase
function func(value : number | string) {
    if(typeof value === "number") {  //→ 타입가드, 다른 타입이 들어오는 것을 막음
        console.log(value.toFixed());
    } else if (typeof value === "string") {
        console.log(value.toUpperCase());
    }
}

 

타입 가드 : 다른 타입이 들어오는 것을 막는다.

이런 코드를 잘 쓰면 내가 의도한대로 프로그램이 작동하게 만들 수 있음

그런데, typeof에는 Date를 쓸 수 없음

string, number, bigint, boolean, symbol, undefined, object, function만 쓸 수 있음

그래서 비슷하게 object를 쓰면 해결되는데, 문제는 null과 같은 값이 껴들어 올 수 있음

그래서 이를 방지하기 위해 instanceof 연산자를 씀

 

2) instanceof 연산자

왼쪽에 있는 값이 오른쪽의 instance냐라고 묻는 연산자

true, false 반환한다.

    } else if (value instanceof Date) {
        console.log(value.getTime());

 

3) 새로운 함수 추가하기

type Person = {
    name : string;
    age : number; 
};

// value => number : toFixed
// value => string : toUpperCase
// value => Date : getTime
// value => Person : name은 age살 입니다.
function func(value : number | string | Date | null | Person) {
    if(typeof value === "number") {  //→ 타입가드, 다른 타입이 들어오는 것을 막음
        console.log(value.toFixed());
    } else if (typeof value === "string") {
        console.log(value.toUpperCase());
    } else if (value instanceof Date) {
        console.log(value.getTime());
    } else if (value && "age" in value) {
        console.log(`${value.name}은 ${value.age}살 입니다.`);
    }
}

Person 타입을 추가해서 원하는 글자를 출력하도록 만들 수도 있다.

더불어 우리가 만든 타입과 함께 사용하려면 in 연산자를 이용해야 한다. ('age" in value)

이 또한 in 타입 가드로 볼 수 있다.

 

 

8강. 서로소 유니온 타입

교집합이 없는 타입들로만 만든 유니온 타입을 말한다.

 

tag가 없다면 admin, member, guest는 모두 교집합을 가지게 된다.

하지만 tag를 쓰는 순간 tag가 admin이면서 member, guest를 동시에 만족시키는 것은 없다.

따라서 아래의 코드는 서로소 유니온이 된다.

type User = Admin | Member | Guest;

 

 

이렇게 되면 코드가 전체적으로 간결해진다.

type Admin = {
    tag : "ADMIN";
    name : string;
    kickCount : number;
};

type Member = {
    tag : "MEMBER";
    name : string;
    point : number;
};

type Guest = {
    tag : "GUEST";
    name : string;
    visitCount : number;
};

type User = Admin | Member | Guest;

// Admin -> {name}님 현재까지 {kickCount}명 강퇴했습니다.
// Member -> {name}님 현재까지 {point} 모았습니다.
// Guest -> {name}님 현재까지 {visitcount}번 방문하셨습니다.

function login(user: User) {
    switch (user.tag) {
        case "ADMIN": {
            console.log(`${user.name}님 현재까지 ${user.kickCount}명 강퇴했습니다.`);
            break;
        }
        case "MEMBER": {
            console.log(`${user.name}님 현재까지 ${user.point} 모았습니다.`);
            break;
        }
        case "GUEST": {
            console.log(`${user.name}님 현재까지 ${user.visitCount}번 방문하셨습니다.`);
            break;
        };
    }
}

코드가 이렇게 작성되면 직관적이기 때문에 서로 알아보기 편하다.

 

type LoadingTask = {
    state : "LOADING";
};

type FailedTask = {
    state : "FAILED";
    error : {
        message : string;
    };
};

type SuccessTask = {
    state : "SUCCESS";
    response : {
        data : string;
    }
}

type AsyncTask = LoadingTask | FailedTask | SuccessTask;

//로딩중 -> 콘솔에 로딩중 출력
//실패 -> 실패 : 에러메시지를 출력
//성공 -> 성공 : 데이터를 출력

function processResult(task : AsyncTask) {
    switch(task.state){
        case "LOADING" : {
            console.log("로딩 중");
            break;
        }
        case "FAILED" : {
            console.log(`에러 발생 : ${task.error.message}`);
            break;
        }
        case "SUCCESS" : {
            console.log(`성공 : ${task.response.data}`);
            break;
        }
    }
}

const loading : AsyncTask = {
    state : "LOADING",
};

const failed : AsyncTask = {
    state : "FAILED",
    error : {
        message : "오류 발생 원인은 ~~",
    },
};

const success : AsyncTask = {
    state : "SUCCESS",
    response : {
        data : "데이터는 ~~",
    },
};

서로소 유니언을 이용하면 로딩 중, 로딩실패, 로딩성공 등 여러 상황에서 직관적인 코드를 쓰기 좋다.

반응형