Closed gwangminjun closed 1 month ago
특징 | is 연산자 | in 연산자 |
---|---|---|
사용 목적 | 타입 가드 함수에서 타입을 추론할 때 사용 | 객체에 속성(key) 존재 여부를 확인할 때 사용 |
사용 방법 | A is B 형태로 매개변수 A가 B 타입이 맞는지 확인(반환 타입에 사용) |
A in B 형태로 A 속성이 B 객체에 있는지 확인 |
확인 대상 | 타입이 특정 타입인지 확인 (string, number, Dog 등) | 객체가 특정 속성 을 가지고 있는지 확인 |
//업로드 시 들어온 파일이 사진인지 영상인지 확인
// html : <input type="file" id="fileInput" />
interface Photo {
type: 'photo';
resolution: string; // 해상도
size: number;
}
interface Video {
type: 'video';
duration: number; // 길이
size: number;
}
document.getElementById('fileInput').addEventListener('change', function(event) {
const file = event.target.files[0];
if (file) {
let mockFile: Photo | Video;
if (file.type.startsWith('image/')) {
mockFile = {
type: 'photo',
resolution: '1920x1080',
size: file.size / (1024 * 1024) // 바이트를 메가바이트로
};
} else if (file.type.startsWith('video/')) {
mockFile = {
type: 'video',
duration: 60, // 1분 설정
size: file.size / (1024 * 1024)
};
} else {
console.error('지원하지 않는 파일 형식입니다.');
return;
}
fileChecking(mockFile);
}
});
function fileChecking(file: Photo | Video) {
if ('resolution' in file) { // ✅
console.log(`사진 해상도 : ${file.resolution}, 사진 용량 : ${file.size}MB`);
} else {
console.log(`영상 길이 : ${file.duration}분, 영상 용량 : ${file.size}MB`);
}
}
function isPhoto(file: Photo | Video): file is Photo {
return file.type === 'photo';
}
function fileChecking(file: Photo | Video) {
if (isPhoto(file)) { // ✅
console.log(`사진 해상도 : ${file.resolution}, 사진 용량 : ${file.size}MB`);
} else {
console.log(`영상 길이 : ${file.duration}분, 영상 용량 : ${file.size}MB`);
}
}
is
value of Type
형태로 사용in
value in Type
형태로 사용is
type OnlineStudy {
platform: string;
startOnlineMeeting: () => void;
}
type OfflineStudy {
location: string;
startOfflineMeeting: () => void;
}
// startOnlineMeeting 메서드가 존재하는지 확인
function isOnlineStudy(study: OnlineStudy | OfflineStudy): study is OnlineStudy {
// startOnlineMeetin이 정의되어 있는지 확인하여 타입 단언(assume)
return (study as OnlineStudy).startOnlineMeeting !== undefined;
}
const onlineStudy: OnlineStudy = {
platform: "Zoom",
startOnlineMeeting: () => {
console.log("Starting an online study.");
}
};
const offlineStudy: OfflineStudy = {
location: "Library",
startOfflineMeeting: () => {
console.log("Starting an offline study.");
}
};
// 스터디 그룹 타입 가드
function manageStudyGroup(study: OnlineStudy | OfflineStudy) {
if (isOnlineStudy(study)) {
// OnlineStudy 타입으로 확인되었으므로 안전하게 메서드 호출
console.log(`The platform is: ${study.platform}`);
study.startOnlineMeeting();
} else {
// OfflineStudy 타입으로 확인되었으므로 안전하게 메서드 호출
console.log(`The location is: ${study.location}`);
study.startOfflineMeeting();
}
}
// 함수를 사용하여 스터디 그룹 관리
manageStudyGroup(onlineStudy); // 온라인 스터디
manageStudyGroup(offlineStudy); // 오프라인 스터디
in
type BookStudy {
bookTitle: string;
readBook: () => void;
}
type VideoStudy {
videoTitle: string;
watchVideo: () => void;
}
function handleStudy(study: BookStudy | VideoStudy) {
// 'bookTitle'이라는 속성이 study 객체에 있는지 확인
if ('bookTitle' in study) {
console.log(`Studying from the book: ${study.bookTitle}`);
study.readBook();
} else {
console.log(`Watching the video: ${study.videoTitle}`);
study.watchVideo();
}
}
// BookStudy 타입의 스터디 자료 객체
const myBookStudy: BookStudy = {
bookTitle: "Learning TypeScript",
readBook: () => {
console.log("Reading TypeScript book!");
}
};
// handleStudyMaterial 함수를 호출하면서 myBookStudy를 전달
handleStudyMaterial(myBookStudy); // BookStudy 타입이므로 책 읽기 로직이 실행됨
특성 | is 연산자 |
in 연산자 |
---|---|---|
용도 | 사용자 정의 타입 가드를 정의하는 데 사용 | 객체에 특정 속성이 존재하는지 확인하는 데 사용 |
구문 | function funcName(param): param is Type |
'property' in object |
대상 | 특정 타입으로 타입을 좁히기 위해 함수나 메서드에서 사용 | 객체에서 속성의 존재 여부를 확인하기 위해 사용 |
반환 타입 | boolean |
boolean |
주로 사용하는 경우 | 복잡한 객체 타입의 검사 , 복잡한 타입 검사 로직을 캡슐화 | 객체에서 특정 필드가 존재하는지 여부를 확인할 때 ,인터페이스나 타입의 구조를 기반으로 타입을 좁힐 때 |
///////////////////////////////////////////
// 'is' 연산자를 사용한 사용자 정의 타입 가드
///////////////////////////////////////////
//두 인터페이스 모두 layEggs 메서드를 가지고 있지만,
//Bird에는 fly 메서드가, Fish에는 swim 메서드를 별도로 가지고 있음
interface Bird {
fly(): void;
layEggs(): void;
}
interface Fish {
swim(): void;
layEggs(): void;
}
//(pet as Fish)는 pet을 Fish로 캐스팅(cast)
//swim 메서드 존재 유무로 pet의 타입 확인
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined;
}
//캐스팅
//프로그래밍에서 특정 데이터 타입을 다른 데이터 타입으로 "변환"하거나 "간주"하는 작업
//if (isFish(pet)) 조건문을 통해 pet이 Fish 타입인지 확인
function moveAnimal(pet: Fish | Bird) {
if (isFish(pet)) {
pet.swim(); // 여기서 pet은 Fish 타입으로 좁혀짐
} else {
pet.fly(); // 여기서 pet은 Bird 타입으로 좁혀짐
}
}
///////////////////////////////////////////
// 'in' 연산자를 사용한 사용자 정의 타입 가드
///////////////////////////////////////////
interface Rectangle {
width: number;
height: number;
}
interface Circle {
radius: number;
}
//유니온 타입 정의
//shape는 Rectangle 타입이거나 Circle 타입일 수 있음
type Shape = Rectangle | Circle;
function calculateArea(shape: Shape) {
if ("width" in shape) {
// 여기서 shape는 Rectangle 타입으로 간주
return shape.width * shape.height;
} else {
// 여기서 shape는 Circle 타입으로 간주
return Math.PI * shape.radius ** 2;
}
}
///////////////////////////////////////////
// 'is', 'in' 연산자 조합
///////////////////////////////////////////
// 두 연산자 조합
interface Developer {
name: string; //공통메서드
code: () => void; //고유메서드
}
interface Designer {
name: string; //공통메서드
design: () => void; //고유메서드
}
interface Manager {
name: string; //공통메서드
manage: () => void; //고유메서드
}
//유니온타입 활용
//Employee가 세 가지 타입 중 하나
type Employee = Developer | Designer | Manager;
//타입가드 함수
//code 메서드가 있으면, employee는 Developer 타입으로 간주
function isDeveloper(employee: Employee): employee is Developer {
return "code" in employee;
}
//design 메서드가 있으면, employee는 Developer 타입으로 간주
function isDesigner(employee: Employee): employee is Designer {
return "design" in employee;
}
function assignTask(employee: Employee) {
if (isDeveloper(employee)) {
employee.code(); // Developer로 타입이 좁혀짐
} else if (isDesigner(employee)) {
employee.design(); // Designer로 타입이 좁혀짐
} else {
employee.manage(); // 나머지 케이스는 Manager로 간주
}
}
// 사용 예
const dev: Employee = { name: "minjun", code: () => console.log("Coding...") };
const designer: Employee = { name: "minjun", design: () => console.log("Designing...") };
const manager: Employee = { name: "minjun", manage: () => console.log("Managing...") };
assignTask(dev); // 출력: Coding...
assignTask(designer); // 출력: Designing...
assignTask(manager); // 출력: Managing...
[공통점]
[차이점] is연산자
[is 연산자 사용예시]
// 텍스트 메시지 타입 정의
interface TextMessage {
type: 'text'; // 메시지 타입
content: string; // 메시지 내용
}
// 이미지 메시지 타입 정의
interface ImageMessage {
type: 'image'; // 메시지 타입
url: string; // 이미지 URL
}
// 비디오 메시지 타입 정의
interface VideoMessage {
type: 'video'; // 메시지 타입
url: string; // 비디오 URL
duration: number; // 비디오 길이 (초 단위)
}
// Message 타입은 위 세 가지 메시지 타입의 유니온
type Message = TextMessage | ImageMessage | VideoMessage;
// 타입 가드 함수: TextMessage인지 확인
function isTextMessage(message: Message): message is TextMessage {
return message.type === 'text'; // 타입이 'text'인지 검사
}
// 타입 가드 함수: ImageMessage인지 확인
function isImageMessage(message: Message): message is ImageMessage {
return message.type === 'image'; // 타입이 'image'인지 검사
}
// 타입 가드 함수: VideoMessage인지 확인
function isVideoMessage(message: Message): message is VideoMessage {
return message.type === 'video'; // 타입이 'video'인지 검사
}
// 메시지 객체 예시 배열
const messages: Message[] = [
{ type: 'text', content: '안녕하세요!' }, // 텍스트 메시지
{ type: 'image', url: 'http://example.com/image.jpg' }, // 이미지 메시지
{ type: 'video', url: 'http://example.com/video.mp4', duration: 120 } // 비디오 메시지
];
// 메시지 처리 함수
function handleMessages(messages: Message[]) {
// 각 메시지를 순회
messages.forEach(message => {
// 텍스트 메시지인 경우
if (isTextMessage(message)) {
console.log(`텍스트 메시지: ${message.content}`);
}
// 이미지 메시지인 경우
else if (isImageMessage(message)) {
console.log(`이미지 메시지: ${message.url}`);
}
// 비디오 메시지인 경우
else if (isVideoMessage(message)) {
console.log(`비디오 메시지: ${message.url} (길이: ${message.duration}초)`);
}
});
}
// 실행: 메시지를 처리
handleMessages(messages);
[in 연산자 사용예시]
// 텍스트 메시지 타입 정의
interface TextMessage {
type: 'text'; // 메시지 타입
content: string; // 메시지 내용
}
// 이미지 메시지 타입 정의
interface ImageMessage {
type: 'image'; // 메시지 타입
url: string; // 이미지 URL
}
// 비디오 메시지 타입 정의
interface VideoMessage {
type: 'video'; // 메시지 타입
url: string; // 비디오 URL
duration: number; // 비디오 길이 (초 단위)
}
// Message 타입은 위 세 가지 메시지 타입의 유니온
type Message = TextMessage | ImageMessage | VideoMessage;
// 메시지 처리 함수
function handleMessages(messages: Message[]) {
// 각 메시지를 순회
messages.forEach(message => {
// 메시지가 TextMessage인 경우
if ('content' in message) {
console.log(`텍스트 메시지: ${message.content}`);
}
// 메시지가 ImageMessage 또는 VideoMessage인 경우
else if ('url' in message) {
// 메시지가 VideoMessage인 경우
if ('duration' in message) {
console.log(`비디오 메시지: ${message.url} (길이: ${message.duration}초)`);
}
// 메시지가 ImageMessage인 경우
else {
console.log(`이미지 메시지: ${message.url}`);
}
}
});
}
// 메시지 객체 예시 배열
const messages: Message[] = [
{ type: 'text', content: '안녕하세요!' }, // 텍스트 메시지
{ type: 'image', url: 'http://example.com/image.jpg' }, // 이미지 메시지
{ type: 'video', url: 'http://example.com/video.mp4', duration: 120 } // 비디오 메시지
];
// 실행: 메시지를 처리
handleMessages(messages);
수고하셨습니다!
📝 p 137 ❓ is 연산자와 in 연산자 를 사용한 타입가드 구현시