웹개발/typescript

[타입스크립트] 타입 가드

joamashi 2024. 9. 2. 21:01

더욱 안전하고 명확한 코드를 위한 필수 도구

타입스크립트는 강력한 타입 시스템을 갖춘 언어로, 개발 중에 오류를 조기에 발견하고 코드의 신뢰성을 높이는 데 도움을 줍니다. **타입 가드(Type Guard)**는 이러한 타입 시스템의 핵심 기능 중 하나로, 런타임에 변수의 실제 타입을 검사하여 코드 블록 내에서 더 정확한 타입 추론을 가능하게 합니다.

왜 타입 가드가 필요할까요?

  • 유연한 타입 시스템: 타입스크립트는 다양한 타입을 지원하지만, 때로는 여러 타입을 가질 수 있는 변수를 다뤄야 할 때가 있습니다.
  • 런타임 오류 방지: 타입 가드를 통해 변수의 타입을 검사하여 예상치 못한 타입으로 인한 오류를 미리 방지할 수 있습니다.
  • 코드 가독성 향상: 명확한 타입 검사를 통해 코드의 의도를 명확하게 표현하고 다른 개발자의 이해를 돕습니다.

타입 가드의 종류와 예시

1. typeof 연산자

  • 기본 타입 검사: number, string, boolean, undefined, object, function 등의 기본 타입을 검사합니다.
function printValue(value: number | string) {
  if (typeof value === 'number') {
    console.log(`숫자입니다: ${value}`);
  } else if (typeof value === 'string') {
    console.log(`문자열입니다: ${value}`);
  }
}

2. instanceof 연산자

  • 객체의 타입 검사: 특정 클래스의 인스턴스인지 확인합니다.
class Person {}
class Student extends Person {}

function isStudent(person: Person): person is Student {
  return person instanceof Student;
}

function greet(person: Person) {
  if (isStudent(person)) {
    console.log(`학생입니다: ${person.name}`);
  } else {
    console.log(`일반인입니다.`);
  }
}

3. in 연산자

  • 인터페이스의 프로퍼티 존재 여부 확인: 특정 인터페이스의 프로퍼티가 존재하는지 확인합니다.
interface Shape {
  kind: 'circle' | 'square';
  radius?: number;
  sideLength?: number;
}

function getArea(shape: Shape) {
  if (shape.kind === 'circle') {
    return Math.PI * shape.radius ** 2;
  } else {
    return shape.sideLength ** 2;
  }
}
 
 

4. 사용자 정의 타입 가드 함수

  • 복잡한 조건: 위에서 설명한 연산자들로는 충분하지 않을 때 사용자 정의 함수를 통해 타입 가드를 만들 수 있습니다.
interface Employee {
  type: 'fulltime' | 'parttime';
  name: string;
  salary?: number;
}

function isFulltimeEmployee(employee: Employee): employee is Employee {
  return employee.type === 'fulltime';
}

왜 타입 가드를 사용해야 할까요?

  • 더 안전한 코드: 런타임 오류를 줄이고 코드의 신뢰성을 높입니다.
  • 더 명확한 코드: 코드의 의도를 명확하게 표현하여 유지보수를 쉽게 합니다.
  • 더 강력한 타입 시스템: 타입스크립트의 타입 시스템을 최대한 활용하여 더욱 정교한 코드를 작성할 수 있습니다.

1. 타입 가드를 사용하여 배열 요소의 타입을 검사하는 방법

배열 요소의 타입을 검사하려면 forEach, map, filter 등의 배열 메서드를 사용하면서 각 요소에 대해 타입 가드를 적용하면 됩니다.

interface Person {
  name: string;
  age: number;
}

interface Animal {
  name: string;
  species: string;
}

function printName(item: Person | Animal) {
  if ('age' in item) {
    console.log(`Person: ${item.name}, ${item.age}세`);
  } else {
    console.log(`Animal: ${item.name}, ${item.species}`);
  }
}

const mixedArray: (Person | Animal)[] = [
  { name: 'Alice', age: 30 },
  { name: 'Bob', species: 'dog' },
];

mixedArray.forEach(printName);

위 예시에서 'age' in item은 item이 Person 타입인지 확인하는 타입 가드 역할을 합니다.

2. 사용자 정의 타입 가드 작성 예시

더 복잡한 조건을 검사하기 위해 사용자 정의 타입 가드 함수를 만들 수 있습니다.

interface Employee {
  type: 'fulltime' | 'parttime';
  name: string;
  salary?: number;
}

function isFulltimeEmployee(employee: Employee): employee is Employee {
  return employee.type === 'fulltime' && employee.salary !== undefined;
}

위 예시에서는 isFulltimeEmployee 함수가 employee가 fulltime 타입이고 salary 프로퍼티가 undefined가 아닌지 확인하는 사용자 정의 타입 가드입니다.

3. 타입 가드와 디스크리미네이트 유니온 타입의 차이점

  • 디스크리미네이트 유니온 타입: 유니온 타입에서 각 타입을 구분하기 위해 공통된 프로퍼티를 사용하는 방법입니다. 컴파일러가 자동으로 타입을 추론해줍니다.
  • 타입 가드: 런타임에 실제 타입을 검사하여 코드 블록 내에서 더 정확한 타입 추론을 가능하게 합니다.
// 디스크리미네이트 유니온 타입
interface Circle {
  kind: 'circle';
  radius: number;
}

interface Square {
  kind: 'square';
  sideLength: number;
}

type Shape = Circle | Square;

function getArea(shape: Shape) {
  if (shape.kind === 'circle') { // 컴파일러가 자동으로 타입을 추론
    // ...
  } else {
    // ...
  }
}

// 타입 가드
function isCircle(shape: Shape): shape is Circle {
  return shape.kind === 'circle';
}

둘의 차이점을 요약하면 다음과 같습니다.

  • 디스크리미네이트 유니온 타입: 컴파일 타임에 타입을 구분하고, 컴파일러가 자동으로 타입을 추론합니다.
  • 타입 가드: 런타임에 타입을 검사하고, 코드 블록 내에서 더 정확한 타입 추론을 가능하게 합니다.

어떤 것을 사용해야 할까요?

  • 디스크리미네이트 유니온 타입: 타입이 명확하게 구분되고 컴파일러가 자동으로 처리해줄 수 있을 때 사용하면 편리합니다.
  • 타입 가드: 더 복잡한 조건이나 런타임에 타입이 결정될 때 사용하면 유용합니다.
728x90