TypeScript기본 타입과 좁히기

assertion function과 custom type predicate

런타임 검사 결과를 타입 시스템에 전달하기 위해 custom type predicate와 assertion function을 어떻게 쓰는지 정리합니다.

마지막 수정 2026년 3월 22일

기본 패턴

ts
function isStringArray(value: unknown): value is string[] {
  return Array.isArray(value) && value.every((item) => typeof item === "string");
}

function assertHasId(value: unknown): asserts value is { id: string } {
  if (!value || typeof value !== "object" || !("id" in value)) {
    throw new Error("id is required");
  }
}

설명

  • TypeScript는 타입 검사 도구이지만, 실제 입력값은 런타임에 들어옵니다. API 응답, JSON.parse, 브라우저 입력 같은 값은 컴파일러가 미리 보장할 수 없습니다.
  • custom type predicate는 "이 함수가 true를 반환했다면 value를 특정 타입으로 봐도 된다"는 사실을 타입 시스템에 알려주는 방식입니다. 반환형에 value is SomeType이 들어갑니다.
  • assertion function은 더 강한 버전입니다. 함수가 정상 종료했다면 값이 특정 타입 조건을 만족한다고 간주하게 만들고, 실패하면 예외를 던집니다. 그래서 validation 이후 코드를 더 간결하게 만들 수 있습니다.
  • 핵심은 타입 단언(as)으로 억지로 통과시키지 말고, 런타임 검사와 타입 좁히기를 연결하는 것입니다. 이 연결이 있어야 안전한 입력 검증 코드가 됩니다.
  • 이런 함수는 결국 프로젝트의 작은 schema 역할을 합니다. 로컬 helper로 끝날 수도 있고, 더 커지면 validation 라이브러리로 옮겨갈 수도 있습니다.

빠른 정리

패턴의미
value is Ttrue일 때 T로 좁힘
asserts value is T함수가 끝나면 T로 간주
잘 맞는 입력API 응답, 사용자 입력, 파싱 결과
피해야 할 방식근거 없는 as 단언

주의할 점

predicate나 assertion function 이름만 그럴듯하고 내부 검사가 부실하면, 컴파일러만 속이고 런타임 안전성은 얻지 못합니다. "무엇을 확인했는지"가 실제 타입 보장 범위와 맞아야 합니다.

참고 링크

1 sources