타입 좁히기(Type Narrowing)와 타입 가드(Type Guard)
타입 좁히기는 변수의 타입을 보다 특정적인 것으로 한정하는 처리이며, 타입 가드는 이러한 타입 좁히기에 사용되는 식을 의미합니다. Steep는 if/else
, 삼항 연산자, 논리 연산자 등을 활용한 타입 좁히기와 is_a?
, instance_of?
와 같은 내장 메서드를 타입 가드로 지원하여 정확한 타입 체크를 수행합니다.
TYPE for Union Types
ActiveSupport
의 present?
와 같이 객체의 존재 여부를 확인하는 메서드는 실행 시점에 유형을 좁히는 역할을 하지만, 기존 Steep에서는 이를 타입 가드로 인식하지 못했습니다. 이에 발표자는 특정 조건을 만족하는 메서드 호출을 타입 가드로 간주하는 ‘TYPE for Union Types’를 제안했습니다. 이 제안은 메서드 호출, 리시버가 변수 또는 순수 메서드 호출, 리시버 타입이 유니온 타입이라는 세 가지 조건을 만족할 경우, 메서드의 반환 값(truthy/falsy)에 따라 리시버의 타입을 좁히도록 합니다. 이 기능은 Steep 1.10에 병합되어 현재 사용 가능합니다.
사용자 정의 타입 가드(User-Defined Type Guards)
애플리케이션에서는 user.admin?
과 같이 사용자 정의 메서드를 통해 클래스나 상태를 판단하는 경우가 많습니다. 그러나 Steep는 이러한 사용자 정의 질의 메서드를 타입 가드로 인식하지 못했습니다. 이를 해결하기 위해 발표자는 RBS 타입 정의에 #A(guard: <predicate>)
형태의 어노테이션을 추가하여 개발자가 직접 메서드를 타입 가드로 지정할 수 있는 ‘사용자 정의 타입 가드’를 제안했습니다. <predicate>
는 self is AdminUser
와 같이 주어(subject), 동사(verb), 타입(type)으로 구성된 규칙을 정의하며, 현재 self is Type
형태만 지원하는 개발 초기 단계에 있습니다.
제약 및 고급 활용
사용자 정의 타입 가드는 타입을 좁히는(sub-type으로 변경) 것만 가능하며, 타입을 넓히는(super-type으로 변경) 것은 허용되지 않습니다. 또한, else
블록에 대한 타입 계산은 아직 완벽하지 않아 실패할 수 있습니다. 하지만 이 기능은 가상 모듈과 결합하여 Article & Published
와 같은 조건부 타입(Conditional Types)을 표현하는 데 활용될 수 있습니다. 이는 ActiveRecord
의 delegated_type
이나 유효성 검사 후 특정 속성이 필수화되는 시나리오 등에서 유용하게 적용될 수 있으며, 향후 RBS Rails
와 같은 타입 제너레이터에 통합되어 자동화될 잠재력을 가집니다.