Gwangju-Web-Study / WoowahanTS_Study

✏️우아한 타입스크립트 with 리액트 스터디
6 stars 2 forks source link

4.2.5 is 연산자와 in 연산자 활용 #19

Closed gwangminjun closed 1 month ago

gwangminjun commented 2 months ago

📝 p 137 ❓ is 연산자와 in 연산자 를 사용한 타입가드 구현시

  1. 공통점
  2. 차이점
  3. 활용예시 를 작성해주세요!
hyeonseong2023 commented 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);
  }
});

in 연산자

function fileChecking(file: Photo | Video) {
  if ('resolution' in file) { // ✅
    console.log(`사진 해상도 : ${file.resolution}, 사진 용량 : ${file.size}MB`);
  } else {
    console.log(`영상 길이 : ${file.duration}분, 영상 용량 : ${file.size}MB`);
  }
}

is 연산자

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`);
  }
}
fe-Jay commented 1 month ago

공통점

차이점

  1. is
    • value of Type 형태로 사용
    • 반환 타입을 명제로 지정하여 타입 지정
  2. in
    • value in Type 형태로 사용
    • 명시적으로 객체 안에 특정 속성의 존재여부 검사.

활용예시

  1. 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);  // 오프라인 스터디
    1. 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 타입이므로 책 읽기 로직이 실행됨
    
gwangminjun commented 1 month ago

is 연산자와 in 연산자

공통점

  1. 타입 가드(Type Guard)로 사용
  2. 컴파일 시간에 타입 체크를 수행하여 타입 안전성 향상
  3. 조건문(if, switch 등) 내에서 사용되어 특정 스코프 내의 타입을 구체화

차이점

특성 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...
BaekWeb commented 1 month ago

[공통점]

[차이점] is연산자

  1. 사용자 정의 타입 가드를 만들 때 사용
  2. 특정 조건을 만족할 때 변수 특정 타입임을 확인하는데 사용 in 연산자
  3. 객체의 특정 속성이 존재하는지 확인할 때 사용
  4. 객체의 프로퍼티에 따라 타입을 좁힐때 유용

[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);
gwangminjun commented 1 month ago

수고하셨습니다!