ReScript in Korean

Bind to Global JS Values

원문

우선 모델링하고자 하는 값이 제공된 API에 존재하지 않는지 확인하세요.

setTimeout 와 같은 몇몇의 자바스크립트 값은 글로벌 영역에 존재합니다. 그리고 아래 처럼 바인딩 할 수 있습니다:

@bs.val external setTimeout: (unit => unit, int) => float = "setTimeout"
@bs.val external clearTimeout: float => unit = "clearTimeout"
// Empty output

(JS.Global 모듈에서 이미 setTimeout, clearTimeout 등을 제공하고 있습니다.)

이것은 자바스크립트의 setTimeout 함수와 이에 해당하는 clearTimeout을 바인딩합니다. external 선언은 setTimeout을 아래처럼 정의하고 있습니다.

  • unit 을 인자로 받고 unit과 integer를 반환하는 함수를 호출하세요.(자바스크립트에서는 아무것도 인자로 받지 않고 아무것도 반환하지 않습니다. 소위 undefined 이라 정의합니다),
  • 여기서 integer 값은 해당 함수를 호출하기 전의 기간을 의미합니다
  • 그리고 timeout의 고유번호인 숫자를 반환합니다. 이 값은 크기가 꽤 크기 때문에, 32 bit 정수형 보다는 float 타입으로 정했습니다.

팁과 트릭

위 내용은 이상적이지 않습니다. 어떻게 setTimeoutfloat를 반환하고, clearTimeout이 인자로 한개를 받아오는 지 봅시다. 여러분이 setTimeout 의 반환값인 float 값을 clearTimeout 에 전달할지는 알 수 없습니다! 모두 알듯이, 여러분 중 누군가는 Math.random() 을 이용하여 setTimeout 반환값을 무작위 숫자로 변환하여 clearTimeout 으로 전달할 수 도 있습니다.

리스크립트는 훌룡한 타입시스템을 가지고 있습니다! 위 문제를 해결하기 위해 널리 사용되는 기능인 추상 유형을 활용해 보겠습니다.

type timerId
@bs.val external setTimeout: (unit => unit, int) => timerId = "setTimeout"
@bs.val external clearTimeout: timerId => unit = "clearTimeout"
let id = setTimeout(() => Js.log("hello"), 100)
clearTimeout(id)
var id = setTimeout(function(param) {
console.log('hello');
}, 100);
clearTimeout(id);

분명히 timeIdsetTimeout에 의해서만 만들어질 수 있는 타입입니다! 이제 clearTimeout은 유효한 ID를 전달받는 것이 보장됩니다. ID값으로 숫자를 사용할지 말지는 내부 구현으로 감추어집니다.

external은 인라인 처리되어있기 때문에, 손으로 쓴 자바스크립트 만큼 읽기 쉬운 자바스크립트 출력값을 얻을 수 있습니다

전역모듈(Global Modules)

글로벌 모듈 영역에서 값을 바인딩하고 싶다면, 아래 예시의 Math.random처럼 bs.scopebs.val external에 붙이세요:

@bs.scope("Math") @bs.val external random: unit => float = "random"
let someNumber = random()
var someNumber = Math.random();

bs.scope에 튜플을 전달하여 임의로 깊은 객체를 바인딩할 수 있습니다:

@bs.val @bs.scope(("window", "location", "ancestorOrigins"))
external length: int = "length"
// Empty output

위 예시는 window.location.ancestorOrigins.length을 바인딩합니다.

특별한 전역값(Special Global Values)

__filename , __DEV__ 와 같은 전역변수는 항상 존재하진 않습니다. 심지어 이러한 전역변수들은 option으로 모델링할 수 없습니다. 리스크립트에서 이런 전역변수를 참조하는 것 만으로도 브라우저 환경에서는 Uncaught ReferenceError: __filename is not defined 오류를 발생시키는 JS 코드가 생성됩니다.

이러한 전역변수의 문제들을 해결하기 위해 리스크립트는 특별한 문법을 제공합니다: %external(a_single_identifier).

switch %external(__DEV__) {
| Some(_) => Js.log("dev mode")
| None => Js.log("production mode")
}
var match = typeof __DEV__ === 'undefined' ? undefined : __DEV__;
if (match !== undefined) {
console.log('dev mode');
} else {
console.log('production mode');
}

첫번째 줄의 typeof 체크는 자바스크립트의 ReferenceError를 발생시키지 않습니다.

다른 예제

switch %external(__filename) {
| Some(f) => Js.log(f)
| None => Js.log("non-node environment")
};
var match = typeof __filename === 'undefined' ? undefined : __filename;
if (match !== undefined) {
console.log(match);
} else {
console.log('non-node environment');
}