Frontend/TypeScript
[TypeScript] TypeScript 공부일지: 서로소 유니온 타입 [6/7 study]
동혁이
2024. 6. 7. 16:21
TypeScript 공부일지: 서로소 유니온 타입
❗️주의
지극히 개인 공부한 내용을 올린 거기 때문에 이해하지 못할 수도 있음
❗️서로소 유니온 타입이란?
- 교집합이 없는 타입들로만 만든 유니온 타입을 말함
- ex) string, number는 교집합이 없음 수학에서는 이런 걸 서로소 집합 이라고 함
- string | number 이런걸 서로소 유니온 타입이라고 부름
/**
* 서로소 유니온 타입
* 교집합이 없는 타입들로만 만든 유니온 타입을 말함
* ex) string, number는 교집합이 없음 수학에서는 이런 걸 서로소 집합 이라고 함
* string | number 이런걸 서로소 유니온 타입이라고 부름
*/
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;
}
}
// if (user.tag === "ADMIN") {
// // 원래 조건문이 "kickCount" in user 이였는데 이제는 user.tag === "ADMIN"이라고 작성하면 직관적임
// // Admin 타입
// console.log(`${user.name}님 ${user.kickCount}명 강퇴했습니다.`);
// } else if ("point" in user) {
// // Member 타입
// console.log(`${user.name}님 현재까지 ${user.point}모았습니다.`);
// } else if ("visitCount" in user) {
// // Guest 타입
// console.log(`${user.name}님 현재까지 ${user.visitCount}번 오셨습니다.`);
// }
// 잘 만들었는데 만약 이걸 다른사람이 보거나 주석도 없다면 이 조건문 내부는 Admin이구나 이걸 아무도 모름
// 결론적으로 코드를 이렇게 작성하면 직관적이지 않음 => 이럴때 서로소 유니온 타입을 사용하면 됨
// 프로퍼티에 tag라는 프로퍼티를 추가할거임 value로는 string 타입이 아니라 string literal type 으로 만들어줌 전부 대문자 사용
}
/**
* 복습겸 한가지 더 사례
* 비동기 작업 처리 결과를 처리하는 객체
*/
type LoadingTask = {
state: "LOADING";
};
type FailedTask = {
state: "FAILED";
error: {
message: string;
};
};
type SuccessTask = {
state: "SUCCESS";
response: {
data: string;
};
};
type AsyncTask = LoadingTask | FailedTask | SuccessTask;
// {
// // state: string; 이거 보다는
// state: "LOADING" | "FAILED" | "SUCCESS";
// error?: {
// message: string;
// };
// response?: {
// data: string;
// };
// };
// 로딩중 => 콘솔에 로딩중 출력
// 실패 => 실패: 에러메시지를 출력
// 성공 => 성공: 데이터를 출력
function processResult(task: AsyncTask) {
switch (task.state) {
case "LOADING": {
console.log("로딩중");
break;
}
case "FAILED": {
// task.error?.message 우리는 case를 정확히 FAILED일때로 했는데 error?.에서 물음표를 빼면 오류가 나옴
// task에 마우스 올려보면 task 일때도 AsyncTask로 나옴 타입이 좁혀지지 않았음
// task.state가 FAILED라 하더라도 error 프로퍼티는 선택적 프로퍼티로 정의 되어있기 때문에
// task에 에러가 있는지 없는지 확실히 모름 쉽게 말하면 좁혀질 타입 자체가 없음 타입이 하나밖에 없으니까
// 어쩔수없이 optional chaining 써주던가 non,null단언을 사용해야하는데 이러면 안전한 코드가 아님
// 이럴때는 AsyncTask를 3개의 타입으로 분리해서 서로소 유니온 타입으로 만들어 줘야함
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: "데이터 ~",
},
};