본문 바로가기
Q&A2026년 4월 18일1분 읽기

TypeScript에서 array.filter(Boolean) 써도 undefined가 타입에서 안 빠져요

프
프론트수진
조회 722댓글 4

배열에서 null/undefined 거르려고 filter(Boolean)을 쓰는데, 결과 타입이 그대로 (string | undefined)[]로 찍힙니다.

const xs: (string | undefined)[] = ["a", undefined, "b"];
const ys = xs.filter(Boolean);     // still (string | undefined)[]
ys.forEach(s => s.toUpperCase()); // ❌ 에러: s가 undefined일 수 있음

filter(Boolean)은 런타임에서는 falsy를 다 거르는데 TypeScript가 왜 이걸 좁혀주지 않는 건가요? tsconfig strict는 true입니다.

댓글 4

김김영삼6개월 전

타입스크립트가 filter 시그니처에 타입 가드 정보를 추론하지 못하기 때문입니다. Boolean은 단지 (v: any) => boolean으로 보일 뿐이라, 리턴 타입이 같은 배열 타입으로 유지됩니다.


방법 1: 타입 가드 명시 (가장 깔끔)
function isDefined<T>(v: T | null | undefined): v is T {
return v !== null && v !== undefined;
}
const ys = xs.filter(isDefined); // string[]

방법 2: NonNullable 캐스트
const ys = xs.filter((v): v is NonNullable<typeof v> => v != null);

방법 3 (5.5+ 공식 해결): Array.prototype.filter의 향상된 추론

TypeScript 5.5부터 filter가 프레디케이트 내부에서 타입 좁힘을 추론하는 능력이 크게 좋아졌습니다. 하지만 Boolean 같은 일반 함수 참조는 여전히 narrow 대상이 되지 않습니다. 인라인 화살표 함수로 쓰면 추론됩니다.


const ys = xs.filter(v => v !== undefined);  // 5.5+에서는 string[]

그럼 왜 Boolean을 막지 않나

JavaScript에서 filter(Boolean)은 관용구라 제거할 수 없고, TS는 런타임 동작을 보존하는 원칙이라 타입 가드가 없는 한 좁히지 않습니다. 공통 유틸로 isDefined 하나 만들어 두면 프로젝트 내내 쓰입니다.

코코딩하는민수6개월 전

저희 팀은 remeda, es-toolkit 같은 유틸 라이브러리 쓰는데 거기 filter는 처음부터 narrow되게 설계돼서 undefined가 자동으로 빠집니다. 커스텀 유틸 늘어나는 게 싫으면 그쪽도 고려해 보세요.

프프론트수진6개월 전

isDefined 유틸을 lib/util/types.ts에 박아두고 썼더니 깔끔해졌습니다. 감사합니다. 그리고 TS 5.5 인라인 화살표 힌트는 처음 알았네요.


하나 더 여쭤보면, filter(x => x) 와 filter(Boolean)은 런타임 동작 완전히 동일한가요? 성능 차이는 무시 가능 수준인가요?

김김영삼프론트수진6개월 전

런타임 동작은 거의 동일합니다. 둘 다 falsy(0, "", null, undefined, NaN, false)를 거르죠. 차이는 함수 호출 오버헤드인데, V8에서는 두 패턴 모두 JIT 최적화 대상이라 실측 차이 0.5% 이내입니다. 성능보다는 "falsy 전체를 거르는 의도인지, undefined만 제외인지"를 코드에서 명확히 하는 쪽이 중요합니다. 숫자 0을 의도적으로 남겨야 한다면 Boolean은 버그의 씨앗이 됩니다.

Ctrl+Enter로 등록