가을별 블로그

Flow comment type 활용

Flow는 정적분석을 통해 JavaScript 코드를 검사하는 타입체커다. TypeScript와 역할도 문법도 닮았지만 Flow만의 기능도 있는데 그 중 하나는 Comment Type이다. TypeScript와 마찬가지로, Flow도 let variableName: typeName꼴의 타입 어노테이션(Type annotation)을 사용하는데, 기존 JavaScript에 없는 구문이다보니 실제로 코드를 실행하기 위해선 먼저 이를 제거하는 과정을 거쳐야 한다. 하지만 Comment Type은 타입 어노테이션을 주석 안에 표기하기 때문에 별도의 과정 없이 곧바로 실행할 수 있다. Flow의 Comment type은 다음과 같은 두 가지 구문이 있다.

// Type include comments:
// /*::    */ 안에는 Flow 문법이 적용된 JavaScript 코드를 넣을 수 있다.

/*::
type Direction = 'left' | 'right' | 'up' | 'down'
*/


// Type annotation comments:
// /*:    */ 는 타입이름이 들어간다.

function move(dir /*: Direction */) {
  // ...
} 

여기서 Type include comments의 경우 보통 type이나 interface 선언을 넣기 위한 용도로 사용하지만, 임의의 JavaScript 코드를 넣을 수 있기에 이를 다르게 활용할 수 있다.

Comment Type + Type Refinement

// @flow
function clearTextarea(textarea /*: HTMLTextAreaElement */) {
  textarea.value = ""
}
document.querySelectorAll("textarea.foo").forEach(textarea => {
  // 이게 없으면 Flow에서 타입오류(incompatible-call)가 발생한다.
  if (!(textarea instanceof HTMLTextAreaElement)) throw "unreachable"
  clearTextarea(textarea)
}) 

Flow는 위 코드에서 forEach내의 textareaHTMLElement로 추론하는데, 이 타입은 곧바로 clearTextarea함수에 넘겨줄 수 없기에 textarea instanceof HTMLTextAreaElement를 사용하여 올바른 타입으로 추론하게끔 만들었다. (Type refinement)

이렇게 하면 Flow에서 정상적으로 통과하지만 뭔가 아쉽다. selector로 "textarea.foo"라고 명시되어있으니 언제나 HTMLTextAreaElement타입일텐데, 굳이 그걸 다시 체크하는 저 if문은 뭔가 불필요해보인다. 이 때 Comment type을 활용하면 어떨까?

// @flow
function clearTextarea(textarea /*: HTMLTextAreaElement */) {
  textarea.value = ""
}
document.querySelectorAll("textarea.foo").forEach(textarea => {
  /*:: if (!(textarea instanceof HTMLTextAreaElement)) throw "unreachable" */
  clearTextarea(textarea)
}) 

이렇게 하면 Flow에서 타입추론도 작동하며, (주석이므로) 브라우저에선 체크과정 없이 곧바로 clearTextarea로 넘어간다. 또한 이 코드를 minify하게 되면 주석을 날려버리기에 결과 코드도 줄어드는 장점도 있다.

#flow #javascript