본문 바로가기
language/typescript

🔎 타입 좁히기(Narrowing) - typeof, in, instanceof 완전 정리

by 죄니안죄니 2025. 4. 27.
📚 타입스크립트 입문 시리즈
🟢 ① tsconfig.json 완전 정복 | 🟢 ② 개발 환경 세팅 | 🟢 ③ 기본 타입 정복 | 🟢 ④ interface vs type | 🟢 ⑤ 함수 타입 선언 | 🟢 ⑥ 유니언 & 인터섹션 타입 | 🟢 ⑦ 타입 추론과 단언 | 🔵 ⑧ 타입 좁히기 (Narrowing)

🔎 타입 좁히기(Narrowing) - typeof, in, instanceof 완전 정리

타입스크립트에서 유니언 타입을 쓸 경우, 특정 타입에 따라 분기처리를 해야 합니다.
이때 사용하는 것이 타입 좁히기 (Narrowing)입니다. 대표적인 narrowing 도구는 다음 3가지입니다:

  • typeof
  • in
  • instanceof

① typeof – 기본 타입 구분에 사용

typeofstring, number, boolean 등 원시 타입 구분에 주로 사용됩니다.

function printValue(value: string | number) {
  if (typeof value === 'string') {
    console.log('문자열 길이:', value.length);
  } else {
    console.log('숫자 제곱:', value * value);
  }
}

가능한 typeof 체크:

typeof value === 'string' | 'number' | 'boolean' | 'undefined' | 'object' | 'function'

② in – 객체 속성 여부로 타입 구분

객체의 특정 속성 존재 여부로 타입을 좁히는 방법입니다.

type Admin = {
  name: string;
  role: string;
};
type User = {
  name: string;
  point: number;
};

function handle(person: Admin | User) {
  if ('role' in person) {
    console.log('관리자:', person.role);
  } else {
    console.log('일반 유저:', person.point);
  }
}

💡 in은 객체 타입 구분 시 가장 많이 쓰이는 실전용 narrowing 도구입니다.


③ instanceof – 클래스 인스턴스 구분

클래스로 생성된 객체(new키워드)를 판단할 때 instanceof를 사용합니다. 해당 객체가 특정 클래스의 인스턴스인지 검사하는 연산자

// OOP 스타일
class Dog {
  bark() {
    console.log('멍멍');
  }
}
class Cat {
  meow() {
    console.log('야옹');
  }
}

function makeSound(animal: Dog | Cat) {
  if (animal instanceof Dog) {
    animal.bark();
  } else {
    animal.meow();
  }
}

const pet = new Dog();

if (pet instanceof Dog) { // new 키워드로 만든 클래스 기반 객체에서 작동
  pet.bark(); // ✅ 가능
}

instanceof는 생성자 기반 타입 체크에 유용합니다. 하지만 함수형 프로그래밍에서는 덜 사용됩니다.

 

✅ instanceof는 생성자 기반에서 유용하지만, 함수형 프로그래밍에선 잘 안 쓰이는 이유는?

함수형 프로그래밍은 보통 다음과 같은 특징이 있어요:

  • 클래스보다 함수와 객체 리터럴 중심
  • new Class() 보다는 { key: value } 또는 factory() 함수 사용
  • 상태를 가지는 인스턴스를 거의 만들지 않음

💡 그래서 함수형에서는 instanceof를 쓰려 해도 비교 대상이 클래스 인스턴스가 아닌 일반 객체이기 때문에 잘 작동하지 않아요.

🔨 예시 비교

// 함수형 스타일
function createDog() {
  return { bark: () => console.log('멍멍') };
}
const dog = createDog();

if (dog instanceof createDog) {
  // ❌ 작동안함: createDog은 생성자가 아님
}

👉 결론:

instanceof는 클래스 중심(OOP) 스타일에서 유용하고,

함수형(FP) 환경에서는 in, typeof, 또는 커스텀 타입 가드(is)를 사용하는 게 더 적절해요.


④ 커스텀 타입 가드 (is)

직접 타입을 구분하는 함수를 만들 수도 있어요. 유니언 타입의 공통 속성이 없을 경우

type Fish = { swim: () => void };
type Bird = { fly: () => void };

function isFish(pet: Fish | Bird): pet is Fish { // 인자가 유니언 타입일 때, B타입이면
  return (pet as Fish).swim !== undefined; // swim속성이 정의되어 있어!. typeScript에게 pet이 확실히 Fish라고 알려주는 증명서
}

function move(pet: Fish | Bird) {
  if (isFish(pet)) {
    pet.swim(); // ✅ Fish로 좁혀짐
  } else {
    pet.fly();
  }
}

//타입체크를 하지 않는 경우 
function move(pet: Fish | Bird) { // 매개변수는 아무거나 들어올 수 있는 경우 공통된 속성만 호출 가능함.
  pet.swim(); // ❌ 에러. pet이 Bird일 수도 있으니까 typeScript는 판별할 수 없음
}

pet is Fish처럼 직접 타입을 판별해주는 **사용자 정의 타입 가드 함수**도 강력한 도구입니다.

 

예시: 에러 응답 판별

type Success = { status: 'ok'; data: string };
type Fail = { status: 'error'; message: string };

function isSuccess(res: Success | Fail): res is Success {
  return res.status === 'ok';
}

function handle(res: Success | Fail) {
  if (isSuccess(res)) {
    console.log(res.data);  // ✅ Success로 타입 좁혀짐
  } else {
    console.log(res.message); // ✅ Fail로 추론됨
  }
}

📐 요약

도구 용도 사용 예시
typeof 기본 타입 구분 typeof x === 'string'
in 속성 존재 여부 'role' in person
instanceof 클래스 인스턴스 구분 x instanceof MyClass
is 타입 가드 사용자 정의 narrowing pet is Fish

📘 다음 글 예고

👉 제네릭(Generic)의 이해와 사용법 – 다양한 타입을 유연하게 다루는 핵심 개념을 배워봅니다.

 

댓글