39일차[Intersection / Type Narrowing]

진하의 메모장·2025년 3월 4일
2

공부일기

목록 보기
45/66
post-thumbnail

2025 / 03 / 04

오늘 수업 시간에는 Intersection과 Type Narrowing에 대해 배웠습니다.
두 내용 다 처음 보는 내용이라 막막하긴 했지만 이해하기 위해 여러 번 읽어보았습니다.
제대로 이해한 것인지는 모르겠지만 최대한 이해하기 쉽게 정리해서 포스팅하겠습니다!



💌 인터섹션 타입

Intersection Type

  • 인터섹션 타입은 여러 타입을 결합해서 하나의 새로운 타입을 만드는 방법입니다.
  • 여러 타입을 & 기호로 결합하여 하나의 객체나 값이 두 가지 이상의 특성을 가지도록 만드는 것을 의미합니다.

1. 기본 사용법

  • 인터섹션 타입을 만들려면 & 기호를 사용합니다.
  • 결합된 타입은 두 타입에 속하는 모든 속성을 갖게 됩니다.
interface Human {
   name: string;
   age: number;
}

interface Contacts {
   phone: string;
   email: string;
}

type User = Human & Contacts;  // Human과 Contacts 타입을 결합

let user1: User = {
   name: "jinha",
   age: 23,
   phone: "010-6551-8374",
   email: "nyang@naver.com",
};
  • User는 Human과 Contacts 두 인터페이스를 결합한 타입입니다.
  • user1은 name, age, phone, email 속성을 모두 가져야 하며, 두 타입을 합친 결과입니다.


2. 원시 타입 결합 인터섹션

  • 원시 타입(number, string 등)을 결합하려고 하면 문제가 발생합니다.
  • number와 string을 동시에 가질 수 없기 때문에 결합할 수 없습니다.
  • 이럴 때는 never 타입이 반환됩니다.
type NumAndString = number & string;  // 결합할 수 없음

// let numAndString: NumAndString = 1;  
// 오류: number와 string은 결합할 수 없음
  • number와 string은 상호 배타적관계에 있습니다.
  • 두 타입을 동시에 만족하는 값은 존재할 수 없습니다.


3. 타입 확장

  • 인터섹션 타입을 활용하면 하나의 타입을 다른 타입으로 확장할 수 있습니다.
type ProductItem = {
   id: number;
   name: string;
   type: string;
};

type ProductItemWithDiscount = ProductItem & {
   discountAmount: number;
};

const item: ProductItemWithDiscount = {
   id: 1,
   name: "item1",
   type: "type1",
   discountAmount: 10,
};
  • ProductItem과 discountAmount를 포함하는 새로운 타입인 ProductItemWithDiscount 타입을 만들었습니다.
  • 기존 타입을 확장하거나 추가할 때 유용합니다.


💌 인터섹션 요약

  • 여러 타입을 & 연산자로 결합하여 새로운 타입을 만들 때 사용됩니다.
  • 결합된 타입은 모든 속성을 포함합니다.
  • 원시 타입(number와 string 등)은 서로 결합할 수 없습니다.
  • 다른 타입을 확장하는 데 유용하게 사용할 수 있습니다.


💌 타입 네로잉

  • 변수가 여러 타입 중 하나를 가질 때, 조건에 따라 타입의 범위를 좁혀주는 것을 의미합니다.
  • 타입 네로잉을 통해 타입 추론이 더 정확하게 이루어집니다.


💌 타입 네로잉 기법

  • 타입 네로잉의 다양한 기법에 대해 예시와 함께 정리해보겠습니다.

1. Assignment Narrowing

할당을 통한 타입 좁히기

  • 변수에 특정 값을 할당함으로써 타입을 좁힙니다.
  • number | string 타입의 변수에 문자열을 할당하면 그 변수는 string 타입으로 좁혀집니다.
let value: number | string = "hello";
value.length;  // string 타입으로 추론됨


2. typeof Narrowing

typeof를 통한 타입 좁히기

  • typeof 연산자를 사용하여 원시 타입을 좁힐 수 있습니다.
  • 해당 값이 string, number, boolean 등 특정 원시 타입인지 확인하고 타입을 좁힙니다.
let value: number | string = Math.random() > 0.5 ? 123 : "hello";

if (typeof value === "string") {
   // value는 이제 string 타입
   console.log(value.length);  // string만 사용 가능
} else {
   // value는 number 타입
   console.log(value.toFixed(2));  // number만 사용 가능
}


3. Truthiness Narrowing

참 / 거짓 판별을 통한 타입 좁히기

  • 값이 null, undefined, false 등 falsy한 값인지를 체크하여 타입을 좁힙니다.
  • 조건문에서 변수가 falsy한 값을 가지면 null 혹은 undefined 타입으로 좁혀주고, truthy한 값을 가지면 그 값의 타입을 유지합니다.
let value: null | string[] = Math.random() > 0.5 ? null : ["hello"];

if (value) {
   // value는 이제 string[] 타입
   console.log(value[0]);  // string[]만 사용 가능
} else {
   // value는 null
   console.log("No data");
}


4. Equality Narrowing

같은 값을 비교하여 타입 좁히기

  • 두 변수가 동일한 값을 가지면 그 타입을 정확히 추론할 수 있습니다.
let value1: number | string = Math.random() > 0.5 ? 123 : "hello";
let value2: string | boolean = Math.random() > 0.5 ? "hello" : true;

if (value1 === value2) {
   // value1과 value2는 이제 string 타입
   console.log(value1.length);  // string만 사용 가능
} else {
   console.log(value1, value2);  // 여전히 number | string, string | boolean
}


5. in Operator Narrowing

in 연산자를 통한 타입 좁히기

  • in 연산자를 사용하여 객체에 특정 프로퍼티가 존재하는지를 확인하고, 타입을 좁힐 수 있습니다.
interface Human {
   name: string;
   age: number;
}

interface Dog {
   name: string;
   type: string;
}

let person: Human = { name: "John", age: 30 };
let dog: Dog = { name: "Buddy", type: "Poodle" };

let humanOrDog: Human | Dog = Math.random() > 0.5 ? person : dog;

if ("type" in humanOrDog) {
   // humanOrDog는 이제 Dog 타입
   console.log(humanOrDog.type);
} else {
   // humanOrDog는 이제 Human 타입
   console.log(humanOrDog.age);
}


6. instanceof Narrowing

instanceof를 통한 타입 좁히기

  • instanceof를 사용하여 객체가 특정 클래스의 인스턴스인지 확인하고, 타입을 좁힐 수 있습니다.
let value: Date | string = Math.random() > 0.5 ? new Date() : "2025-03-05";

if (value instanceof Date) {
   // value는 Date 타입
   console.log(value.toISOString());
} else {
   // value는 string 타입
   console.log(value.toUpperCase());
}


7. 구분된 유니온

구분된 유니온을 통한 타입 좁히기

  • 구분된 유니온(Discriminated Union)은 각 타입에 고유한 값을 할당하여, switch문이나 if문으로 타입을 좁힐 수 있는 방법입니다.
type Dog = { type: "dog"; name: string };
type Cat = { type: "cat"; name: number };

let animal: Dog | Cat = Math.random() > 0.5 ? { type: "dog", name: "Buddy" } : { type: "cat", name: 123 };

switch (animal.type) {
   case "dog":
      // animal은 Dog 타입
      console.log(animal.name);  // string
      break;
   case "cat":
      // animal은 Cat 타입
      console.log(animal.name);  // number
      break;
   default:
      const check: never = animal;  // never 타입을 사용해 안전성 보장
}


💌 실습 예제

  • 오늘 배운 내용을 가지고 간단한 예제 문제를 풀어보았습니다.

1. 숫자 또는 문자열 판별하기

  • 숫자 또는 문자열을 받은 함수 작성합니다.
  • typeof를 사용하여 네로잉 구현합니다.
/**
function formatValue(value: string | number): string {
  / 여기에 타입 내로잉을 활용해 구현하세요./
}

결과 : 
console.log(formatValue(42));       // "숫자: 42"
console.log(formatValue("hello"));  // "문자열: hello"
*/
function formatValue(value: string | number): string {
   if (typeof value === "string") {
      return `문자열: ${value}`; // 문자열일 경우
   } else {
      return `숫자: ${value}`; // 숫자일 경우
   }
}

console.log(formatValue(42));
console.log(formatValue("hello"));


2. 객체 타입 구별하기

  • in 연산자를 활용합니다.
/**
type Car = { fuel: string };
type Bike = { pedal: boolean };
function getVehicleType(vehicle: Car | Bike): string {
  // 타입 내로잉을 활용해 구현
}

결과 : 
console.log(getVehicleType({ fuel: "Gasoline" }));  // "자동차 연료: Gasoline"
console.log(getVehicleType({ pedal: true }));       // "자전거는 페달이 있음"
*/
type Car = { fuel: string };
type Bike = { pedal: boolean };

function getVehicleType(vehicle: Car | Bike): string {
   if ("fuel" in vehicle) {
      return `자동차 연료 : ${vehicle.fuel}`;
   } else {
      return `자전거는 페달이 있음`;
   }
}

console.log(getVehicleType({ fuel: "Gasoline" }));
console.log(getVehicleType({ pedal: true }));


3. 유니온 타입으로 좁히기

  • 구분된 유니온을 활용합니다.
/**
type Dog = { type: "dog"; sound: string };
type Cat = { type: "cat"; sound: string };
type Bird = { type: "bird"; sound: string };

function getAnimalSound(animal: Dog | Cat | Bird): string {
  // 타입 내로잉을 활용해 구현하세요.
}

결과 : 
console.log(getAnimalSound({ type: "dog", sound: "멍멍" }));  // "멍멍"
console.log(getAnimalSound({ type: "cat", sound: "야옹" }));  // "야옹"
console.log(getAnimalSound({ type: "bird", sound: "짹짹" })); // "짹짹"
*/
type Dog = { type: "dog"; sound: string };
type Cat = { type: "cat"; sound: string };
type Bird = { type: "bird"; sound: string };

function getAnimalSound(animal: Dog | Cat | Bird): string {
   switch (animal.type) {
      case "dog":
         return animal.sound;
      case "cat":
         return animal.sound;
      case "bird":
         return animal.sound;
      default:
         return "";
   }
}

console.log(getAnimalSound({ type: "dog", sound: "멍멍" }));
console.log(getAnimalSound({ type: "cat", sound: "야옹" }));
console.log(getAnimalSound({ type: "bird", sound: "짹짹" }));



39일차 후기

  • 인터섹션을 배울 때까지는 괜찮았는데 타입 네로잉은 종류가 많아서 배우다가 살짝 헷갈리는 부분도 생겨서 복습을 여러 번 해야겠다고 생각했습니다..૮( ྀིˊ ᵔ ˋ)ა
  • 오늘 내용도 어려운 부분은 별로 없었는데 이론을 배우고 바로 실습 문제에 적용해보려고 하다보니 멈칫하는 부분은 어쩔 수 없이 생기는 것 같습니다.
  • 그래도 고민해서 결국은 원하는 결과 값을 얻어냈습니다!! 기분이 너무 좋았습니다.
  • 수업 들으면서 생각했는데 "네로잉"이라는 이름이 참 귀여운 것 같습니다. ✧٩(ˊωˋ*)و✧
profile
૮꒰ ྀི〃´꒳`〃꒱ა

0개의 댓글