본문 바로가기

TypeScript

TypeScript에서 isEmpty 함수로 타입 좁히기 가능 여부

반응형

TypeScript에서 isEmpty 함수로 타입 좁히기 가능 여부

Type Guard와 타입 좁히기의 동작 원리

TypeScript에서는 **타입 가드(type guard)**를 통해 조건문 내부에서 변수의 타입을 좁혀서 추론합니다. 타입 가드란 런타임에 특정 확인을 수행하여, 해당 조건이 true인 경우 변수의 타입을 보다 구체적인 타입으로 한정할 수 있는 표현식입니다. 대표적인 예로 typeof, instanceof, in 연산자나 엄격한 비교 (=== null 등)가 있으며, 사용자 정의 함수도 반환 타입에 *타입 판별자(type predicate)*를 명시하면 타입 가드로 동작합니다typescriptlang.orgtypescriptlang.org.

 

하지만 중요한 제약이 있습니다. 타입스크립트의 제어 흐름 분석(control flow analysis)은 함수 경계를 넘어서는 내부 구현까지 추적하지 않습니다stackoverflow.com. 즉, 단순히 반환 타입이 boolean인 임의의 함수 호출은 타입 가드로 인식되지 않습니다. 예를 들어, 코드를 직접 작성하여 if (x === undefined || x.length === 0) return;처럼 함수 내부에 조건식을 작성하면 해당 블록 이후 x의 타입이 좁혀지지만, 그 로직을 별도 함수로 추출하여 if (isEmpty(x)) return;으로 호출하면 함수 내부를 컴파일러가 추론하지 못하기 때문에 타입이 좁혀지지 않습니다stackoverflow.com. 이러한 한계를 보완하기 위해 TypeScript는 함수의 반환 타입에 parameterName is Type 형태의 타입 판별자를 명시하는 사용자 정의 타입 가드 문법을 제공합니다typescriptlang.orgtypescriptlang.org.

Lodash _.isEmpty의 타입 정의와 한계

Lodash의 _.isEmpty 함수는 다양한 값이 "비어있는지" 여부를 판단하는 유틸리티입니다. 그러나 타입 정의 상 반환 타입이 단순 boolean으로 되어 있어 타입 가드로 선언되어 있지 않습니다endpointdev.com. Lodash에서 제공하는 다른 함수 _.isUndefined의 경우 타입 정의가 value is undefined로 지정되어 있어 true일 때 인수가 undefined임을 컴파일러가 알 수 있지만, _.isEmpty는 그렇지 않습니다endpointdev.com.

 

이로 인해 strictNullChecks 등의 옵션이 활성화된 환경에서는 아래와 같은 코드가 컴파일 오류를 일으킵니다:

function runEscapeIfNotEmpty(container?: Container) {
  if (_.isEmpty(container)) {
    return;
  }
  container.escape();  // 컴파일 오류: container가 여전히 undefined일 수 있음
}

위 코드에서 _.isEmpty(container)가 false인 경우 논리적으로 container는 undefined나 null이 아니어야 하지만, 컴파일러는 이를 추론하지 못하고 여전히 container를 Container | undefined로 간주합니다endpointdev.com. 이는 _.isEmpty가 타입 가드로 선언되어 있지 않고, TypeScript가 해당 함수 호출의 내부 로직을 고려한 좁히기를 수행하지 않기 때문입니다stackoverflow.com.

tsconfig.json을 통한 전역 설정 가능 여부

결론부터 말하면, TypeScript 컴파일러에 전역적으로 특정 함수(isEmpty 등)를 타입 가드로 취급하도록 지시하는 tsconfig 옵션은 존재하지 않습니다. TypeScript의 타입 검사 동작은 언어 사양에 고정되어 있으며, 임의의 함수에 대한 좁히기 규칙을 전역 설정으로 추가할 수는 없습니다. 즉, tsconfig.json이나 컴파일러 플래그를 통해 "이 함수의 반환이 false면 인자가 undefined가 아니다"와 같은 규칙을 알려줄 방법은 없습니다. 공식 문서나 마이크로소프트의 TypeScript 설정 레퍼런스를 살펴봐도, 특정 함수명을 지정하여 타입 가드로 동작하게 하는 옵션은 제공되지 않습니다.

 

또한 JSDoc 주석만으로 일반 함수의 반환을 타입 가드로 만드는 것도 불가능합니다. JSDoc으로 타입 정보를 보강할 수는 있지만, 타입 판별자(x is SomeType)는 TypeScript 타입 표기 문법이므로 함수 시그니처 자체를 타입 가드 형태로 선언하지 않는 한 효과가 없습니다. Lodash의 isEmpty를 JSDoc으로 설명한다고 해서 컴파일러 동작이 바뀌지 않습니다.

사용자 정의 타입 가드 함수와 언어 서비스 플러그인

전역 설정이 불가능하다 보니, 이 문제를 해결하려면 코드 단에서 직접 타입 가드 함수를 작성해야 합니다. 예를 들어 Lodash를 감싸거나 유사한 vIsEmpty 함수를 만들어 아래처럼 타입 판별자를 명시할 수는 있습니다:

function vIsEmpty<T>(value: T): value is /* Empty Type */ {
  // ... 구현 (value가 비어있으면 true 반환)
}

이처럼 반환 타입을 value is ...로 선언하면 if (!vIsEmpty(x)) 블록 안에서 x의 타입이 좁혀집니다. 그러나 “빈 값”이라는 개념을 타입으로 정확히 표현하기가 어려워 이러한 타입 가드의 선언은 매우 복잡해집니다. 예컨대, null, undefined, false, 0, "" (빈 문자열), [] (빈 배열), {} (빈 객체) 등을 모두 포괄하려면 해당 경우들을 합집합으로 묶은 별도의 타입을 정의해야 합니다stackoverflow.comstackoverflow.com. 그럼에도 불구하고 완벽하게 표현할 수 없거나(예: NaN이나 특수한 문자열 케이스) 컴파일러가 한쪽 분기만 좁히는 한계가 존재합니다stackoverflow.comstackoverflow.com. 실제로 DefinitelyTyped 저장소의 Lodash 타입 정의 이슈에서도, _.isEmpty를 타입 가드로 만들려는 시도가 “불가능하다”고 결론지어진 바 있습니다github.com. Lodash 유지보자들도 isEmpty의 타입 가드화를 여러 차례 요청받았지만 구현상 불가능하다는 입장을 밝히고 있습니다github.com. 핵심 이유는 타입 가드의 한쪽 분기만 정의하는 것(one-sided type guard)이 지원되지 않기 때문입니다. isEmpty를 타입 가드로 만든다면 true일 때 "비어있음"을 의미하도록 정의해야 하는데, 그러면 false일 때 반대로 모든 빈 값이 아님(null/undefined 아님 뿐만 아니라 빈 배열/빈 객체도 아님)을 의미하도록 받아들여져 타입 추론에 오류를 일으키게 됩니다github.com. TypeScript에는 value is not Type과 같은 식으로 “아니다”를 표현하는 타입 판별자를 제공하지 않아 이러한 세밀한 제어가 불가능합니다endpointdev.com.

 

일부 고급 사용자는 TypeScript 언어 서비스 플러그인을 통해 추가적인 검사 규칙을 넣는 방법을 생각해볼 수 있습니다. 그러나 언어 서비스 플러그인은 주로 IDE 상의 피드백이나 보조 진단을 제공할 수 있을 뿐, TypeScript 컴파일러의 기본적인 타입 판정 규칙을 근본적으로 바꾸지는 못합니다. 즉, 플러그인으로 특정 함수 호출 시 타입을 좁히는 기능을 전역적으로 추가하는 것은 현실적으로 쉽지 않고, 공식적으로 지원되는 방향도 아닙니다.

공식 입장과 근거 자료

TypeScript 개발진과 커뮤니티에서도 이 주제에 대한 논의가 있었으며, 결론은 **“전역적으로 임의의 함수를 타입 가드로 지정하는 기능은 지원되지 않는다”**는 것입니다. 앞서 언급한 것처럼, DefinitelyTyped의 Lodash 이슈(#45521)에서 **“이것은 여러 번 요청되었지만 구현상 불가능하다”**는 maintainer의 답변이 그 근거 중 하나입니다github.com. 또 다른 자료인 End Point Dev 블로그 글에서도 Lodash isEmpty 예시를 들며, **TypeScript에 'is not' 연산자가 없어** isEmpty를 이상적으로 value is not undefined | null형태로 정의할 수 없다는 점을 지적합니다:contentReference[oaicite:19]{index=19}. 이로 인해 컴파일러가isEmpty호출 후undefined/null`을 배제하지 못하며, 이러한 한계는 현 시점의 TypeScript에서 극복할 수 없는 제약임을 설명하고 있습니다endpointdev.comendpointdev.com.

 

아울러 타입스크립트 핵심 설계 원리에 따르면, 함수 내부 구현을 컴파일러가 추론하지 않고 오직 함수의 시그니처(선언된 타입)만 신뢰하기 때문에, 전역 설정으로 함수를 좁히게 만들 수 없다는 것이 명확합니다stackoverflow.com. TypeScript 팀의 공식 문서에도 사용자 정의 타입 가드를 만드는 방법이 안내되어 있을 뿐, 별도의 컴파일러 옵션에 대한 언급은 없습니다typescriptlang.orgtypescriptlang.org.

결론

TypeScript 4.x 기준으로, _.isEmpty()나 사용자가 정의한 유사 함수에 대해 별도의 tsconfig 설정만으로 해당 함수를 타입 좁히기로 인식시키는 방법은 존재하지 않습니다. JSDoc 주석이나 단순 전역 설정으로 해결할 수 없으며, 현재 TypeScript 컴파일러의 한계로 받아들여야 합니다github.comendpointdev.com. 결국 해결책은 함수 자체를 타입 가드로 선언하는 것뿐인데, Lodash isEmpty의 경우 다형적인 “빈” 개념을 타입으로 표현하기 어렵고, 설령 표현하더라도 한쪽 분기의 잘못된 추론 문제로 인해 DefinitelyTyped에서도 채택되지 않았습니다github.com. 따라서 현 시점에서는 사용자 코드에서 직접 myArray && !_.isEmpty(myArray) 같이 명시적으로 null 체크를 병행하거나, 용도에 맞는 커스텀 타입 가드 함수를 작성하여 사용하는 수밖에 없습니다. 이러한 제약은 공식적으로 확인된 사항이며, TypeScript 컴파일러는 전역 설정보다는 개별 함수의 명시적인 타입 표기를 통해서만 타입 좁히기를 지원한다는 점을 기억해야 합니다stackoverflow.comendpointdev.com.


인용
 

TypeScript: Documentation - Advanced Types

 

 

TypeScript: Documentation - Advanced Types

 

types - How can I write my own isEmpty function so the typescript compiler knows a false result means the parameter IS defined? - Stack Overflow

 

Type guards in TypeScript | End Point Dev

 

Type guards in TypeScript | End Point Dev

 

types - How can I write my own isEmpty function so the typescript compiler knows a false result means the parameter IS defined? - Stack Overflow

 

types - How can I write my own isEmpty function so the typescript compiler knows a false result means the parameter IS defined? - Stack Overflow

 

types - How can I write my own isEmpty function so the typescript compiler knows a false result means the parameter IS defined? - Stack Overflow

 

types - How can I write my own isEmpty function so the typescript compiler knows a false result means the parameter IS defined? - Stack Overflow

 

[@types/lodash] !isEmpty should tell typescript variable is not null · Issue #45521 · DefinitelyTyped/DefinitelyTyped · GitHub

 

Type guards in TypeScript | End Point Dev

 

Type guards in TypeScript | End Point Dev

반응형