2026. 7. 16.

TypeScript 'err' is of type 'unknown', Error로 좁히는 순서

TypeScript catch 안에서 'err is of type unknown' 오류가 뜰 때 instanceof Error와 fallback으로 메시지를 안전하게 꺼내고 타입 검사까지 통과하는 순서입니다.

3 min read
TypeScript 'err' is of type 'unknown', Error로 좁히는 순서 대표 이미지

catch (err) 안에서 err.message에 빨간 줄이 생기고 TypeScript가 'err' is of type 'unknown'이라고 알리면, any를 붙여 경고부터 없애고 싶어진다. 그러나 이 오류는 catch 문이 틀렸다는 뜻이 아니다. 잡힌 값이 실제 Error인지 아직 확인하지 않았다는 뜻이다. 먼저 instanceof Error로 확인하고, Error가 아닌 값에는 별도 fallback을 주면 된다.

TypeScript catch 블록의 err.message 오류를 확인하는 개발 작업 화면

err.message보다 먼저 타입을 확인한다

가장 짧은 수정은 아래 두 갈래다. Error 인스턴스라면 message를 읽고, 아니라면 안전한 기본 문구를 쓴다.

try {
  await saveProfile();
} catch (err) {
  const message = err instanceof Error
    ? err.message
    : "알 수 없는 오류가 발생했습니다.";

  console.error(message);
}

TypeScript의 useUnknownInCatchVariables 문서는 잡힌 값이 Error의 하위 클래스라고 보장할 수 없기 때문에 사용 전에 확인해야 한다고 설명한다. instanceof Error 분기 안에서는 TypeScript가 errError로 좁히므로 message, name, stack 같은 속성에 접근할 수 있다.

unknown 값이 Error 분기와 문자열 fallback으로 갈리는 타입 좁히기 흐름

여기서 확인 없이 err as Error로 바꾸지 않는다. 타입 단언은 런타임 값을 검사하지 않는다. 누군가 throw "timeout"처럼 문자열을 던졌다면 as Error를 써도 실제 message 속성은 생기지 않는다.

unknown은 오류가 아니라 확인 대기 상태다

JavaScript에서는 throw new Error("실패")뿐 아니라 문자열, 숫자, 객체도 던질 수 있다. catch는 이 값을 모두 받아야 한다. TypeScript 4.4 릴리스 노트는 이 이유로 unknownany보다 안전한 기본값이라고 설명한다.

strict를 켠 프로젝트에서는 useUnknownInCatchVariables도 함께 활성화된다. 예전 예제나 느슨한 설정에서 err.message가 바로 통과했는데 새 프로젝트에서 빨간 줄이 뜨는 이유가 여기에 있다. 설정을 끄는 것보다 값의 모양을 확인하는 코드를 한 줄 추가하는 편이 오류 처리까지 타입 검사를 받는 방법이다.

AI가 오류 목록을 넓게 고치려 한다면 먼저 TypeScript 첫 오류 하나로 수정 범위를 좁히는 순서를 적용한다. 첫 오류가 정말 catch 변수에서 시작하는지 재현한 뒤 이 패턴만 넣어야 다른 파일까지 바뀌는 일을 막을 수 있다.

메시지 변환 함수를 하나 두면 반복이 줄어든다

catch 블록이 여러 곳이라면 같은 삼항 연산자를 복사하지 말고 입력은 unknown, 출력은 string인 작은 함수를 둔다.

export function getErrorMessage(error: unknown): string {
  if (error instanceof Error) {
    return error.message;
  }

  if (typeof error === "string") {
    return error;
  }

  return "알 수 없는 오류가 발생했습니다.";
}

사용하는 쪽은 오류 처리의 목적만 남긴다.

try {
  await saveProfile();
} catch (error) {
  setNotice(getErrorMessage(error));
}

이 함수는 Error, 문자열, 그 밖의 값이라는 세 경우를 분리한다. 객체 전체를 JSON.stringify해 사용자에게 보여 주지는 않는다. 응답 객체에 토큰이나 내부 경로가 섞일 수 있고, 순환 참조 때문에 변환 자체가 실패할 수도 있다.

Axios처럼 전용 오류 타입을 제공하는 라이브러리에서 responsecode까지 읽어야 한다면 공식 타입 가드를 한 단계 더 둔다. 기본 message만 필요할 때와 라이브러리 전용 속성이 필요할 때를 구분해야 한다. AI에게 수정을 맡길 때는 오류 문구와 관련 파일을 네 칸으로 전달하는 방법처럼 원하는 속성과 수정 범위를 함께 적는다.

타입 검사까지 통과해야 수정이 끝난다

에디터의 빨간 줄이 사라져도 프로젝트 전체가 통과했다는 뜻은 아니다. 저장한 뒤 프로젝트의 typecheck 스크립트를 먼저 실행한다.

npm run typecheck

스크립트가 없다면 TypeScript가 설치된 프로젝트에서 아래 명령을 실행한다.

npx tsc --noEmit
TypeScript unknown 오류 수정 뒤 확인할 네 단계 체크리스트

완료로 볼 수 있는 상태

  • 같은 catch 줄의 TS18046 또는 message 속성 오류가 사라졌다.
  • npx tsc --noEmit 또는 프로젝트 typecheck가 종료 코드 0으로 끝났다.
  • Error가 아닌 값을 던지는 작은 테스트에서도 fallback 문구가 나온다.

더 구체적인 검사가 필요한 상태

  • API 응답의 status, response, code를 읽어야 한다.
  • iframe이나 다른 JavaScript 실행 영역에서 넘어온 오류를 다룬다.
  • 오류 종류에 따라 재시도, 로그아웃, 결제 취소처럼 다른 행동을 해야 한다.

MDN의 instanceof 문서는 이 연산자가 prototype chain을 검사하며, 서로 다른 실행 영역에서는 기대와 다른 결과가 날 수 있다고 설명한다. 그런 경계에서는 instanceof Error 하나로 모든 오류 형식을 판정한다고 단정하지 말고 라이브러리의 공식 guard나 직접 만든 속성 검사를 사용한다.

오늘 할 일은 실패한 catch 한 곳에 getErrorMessage를 적용하고 typecheck를 다시 실행하는 것이다. 종료 코드 0과 fallback 동작을 모두 확인하면 수정이 끝난다.

참고 출처

이 글은 AI 코딩과 개발 학습의 일반 정보 제공 목적입니다. 도구, 모델, 커리큘럼, 요금은 버전과 시점에 따라 달라질 수 있으므로 실습이나 도입 전 공식 문서와 최신 릴리스 노트를 확인하세요.

다음으로 읽을 기사

같은 흐름으로 이어 읽기 좋은 기사만 추려 보여줍니다.

댓글 0

이 글을 읽은 독자들의 생각을 나눠보세요.

비밀번호(선택)

첫 번째 댓글을 남겨보세요

여러분의 생각이 다른 독자에게 도움이 됩니다.