커스텀 타입 가드는 is 키워드로 "이 함수가 true를 반환하면 매개변수의 타입이 좁혀진다"고 TypeScript에게 알려주는 것입니다.

타입 가드 함수 (Type Predicate)

TYPESCRIPT
// 반환 타입이 'value is string' — 타입 술어(Type Predicate)
function isString(value: unknown): value is string {
  return typeof value === 'string';
}

const input: unknown = 'hello';

if (isString(input)) {
  // input: string — 타입이 좁혀짐
  console.log(input.toUpperCase());
}

일반 boolean 반환과의 차이

TYPESCRIPT
// ❌ 일반 boolean — 타입이 좁혀지지 않음
function isStringBool(value: unknown): boolean {
  return typeof value === 'string';
}

const data: unknown = 'test';
if (isStringBool(data)) {
  // data: unknown — 여전히 unknown!
  // data.toUpperCase(); // Error
}

// ✅ 타입 가드 — 타입이 좁혀짐
function isStringGuard(value: unknown): value is string {
  return typeof value === 'string';
}

if (isStringGuard(data)) {
  // data: string — 좁혀짐!
  data.toUpperCase(); // OK
}

객체 타입 가드

TYPESCRIPT
interface User {
  type: 'user';
  name: string;
  email: string;
}

interface Admin {
  type: 'admin';
  name: string;
  permissions: string[];
}

type Person = User | Admin;

function isAdmin(person: Person): person is Admin {
  return person.type === 'admin';
}

function showInfo(person: Person) {
  if (isAdmin(person)) {
    console.log('권한:', person.permissions); // OK — Admin
  } else {
    console.log('이메일:', person.email); // OK — User
  }
}

복잡한 검증 로직

TYPESCRIPT
interface ApiResponse {
  data: unknown;
  status: number;
}

interface UserData {
  id: number;
  name: string;
  email: string;
}

// 런타임 검증을 타입 가드로 래핑
function isUserData(data: unknown): data is UserData {
  if (typeof data !== 'object' || data === null) return false;

  const obj = data as Record<string, unknown>;
  return (
    typeof obj.id === 'number' &&
    typeof obj.name === 'string' &&
    typeof obj.email === 'string'
  );
}

// 사용
async function fetchUser(): Promise<UserData | null> {
  const response = await fetch('/api/user');
  const data = await response.json();

  if (isUserData(data)) {
    return data; // UserData로 안전하게 반환
  }
  return null;
}

배열 필터링에서의 타입 가드

TYPESCRIPT
const items: (string | null | undefined)[] = ['hello', null, 'world', undefined, 'ts'];

// ❌ 일반 filter — (string | null | undefined)[]
const filtered = items.filter((item) => item != null);
// 타입이 여전히 (string | null | undefined)[]

// ✅ 타입 가드 filter — string[]
const filtered2 = items.filter((item): item is string => item != null);
// string[]

면접에서 "filter로 null을 제거했는데 타입이 안 좁혀져요"라는 문제의 해결법으로 자주 나옵니다.

Assertion Functions (asserts)

asserts 키워드를 사용하면 함수가 실패 시 예외를 던지고, 성공하면 타입을 좁히는 패턴을 구현할 수 있습니다.

TYPESCRIPT
function assertIsString(value: unknown): asserts value is string {
  if (typeof value !== 'string') {
    throw new Error(`Expected string, got ${typeof value}`);
  }
}

const data: unknown = 'hello';
assertIsString(data);
// 이 줄 이후부터 data: string
console.log(data.toUpperCase()); // OK

assert 함수 만들기

TYPESCRIPT
// 일반적인 assert 함수
function assert(condition: unknown, message?: string): asserts condition {
  if (!condition) {
    throw new Error(message ?? 'Assertion failed');
  }
}

const user: User | null = getUser();
assert(user !== null, '사용자를 찾을 수 없습니다');
// user: User — null이 제거됨
console.log(user.name); // OK

Node.js의 assert와의 관계

TYPESCRIPT
import assert from 'node:assert';

const value: string | number = getValue();
assert(typeof value === 'string');
// value: string — Node.js의 assert도 타입을 좁힘

is vs asserts

TYPESCRIPT
// is — if 문에서 사용, 타입을 좁히거나 좁히지 않거나
function isNumber(value: unknown): value is number {
  return typeof value === 'number';
}

if (isNumber(data)) {
  // 좁혀짐
} else {
  // 좁혀지지 않음
}

// asserts — 함수 호출 이후 무조건 좁혀짐 (실패하면 예외)
function assertNumber(value: unknown): asserts value is number {
  if (typeof value !== 'number') throw new Error('Not a number');
}

assertNumber(data);
// 이 줄 이후 data: number (예외 안 났으면)
특성is (Type Predicate)asserts
사용 위치if/else 조건함수 호출 이후
실패 시else 분기로 이동예외 발생
용도조건부 타입 좁히기사전 조건 검증

정리

  • 커스텀 타입 가드는 value is Type으로 반환 타입을 지정해서 타입을 좁힌다
  • 일반 boolean 반환으로는 타입이 좁혀지지 않는다
  • filter에서 타입 가드를 쓰면 배열의 타입이 좁혀진다
  • asserts 함수는 실패 시 예외를 던지고, 성공하면 이후 코드에서 타입을 좁힌다
  • is는 조건부, asserts는 전제 조건 검증에 사용한다
댓글 로딩 중...