Как бороться со значениями Nullable в фасадах scala.js? - PullRequest
2 голосов
/ 27 октября 2019

Я пытаюсь написать фасад Scalajs для библиотеки request , в которой есть метод с использованием шаблона обратного вызова:

request('http://www.google.com', function (error, response, body) {
  console.log('error:', error); // Print the error if one occurred
  console.log('statusCode:', response && response.statusCode); // Print the response status code if a response was received
  console.log('body:', body); // Print the HTML for the Google homepage.
});

В этом шаблоне либо error равно нулю, аresponse и body определены или наоборот.

Как лучше всего отразить этот рисунок на фасаде ScalaJS? Лучшее, что я могу придумать, это:

@js.native
@JSImport("request", JSImport.Default)
object Request extends js.Object {
  def apply[A](uri: String,
               requestConfig: js.Object,
               callback: js.Function2[js.JavaScriptException, Response, A]): Unit = js.native
}

После выполнения метода я затем использую Option, чтобы сопоставить правильный регистр:

Request(url, RequestConfig(queryString, headers), (error, response) => {
  (Option(error), Option(response)) match {
    case (Some(err), _) => // handle error
    case (_, Some(res)) => // handle success
    case (None, None) => // This will only happen if there is a bug in the request library
  }
})

Мне это не нравится, потому что1) Я должен написать ненужное совпадение для (None, None) или проигнорировать предупреждение о неисчерпывающем совпадении и 2) фасад не точно описывает типы.

Я также пытался использовать js.UndefOr[js.JavaScriptException], но этовозвращает Some(null) из .toOption и js.JavaScriptException | Null, но я могу только преобразовать это в Option[js.JavaScriptException | Null].

1 Ответ

1 голос
/ 08 ноября 2019

В отличие от undefined, Scala.js не дает вам специальных инструментов для работы с null.

Это потому, что в Scala все обнуляется (многим из нас не нравится этот факт, включая меня), но это другое обсуждение).

Поэтому мы должны утверждать, что фасад действительно точно описывает типы в максимально возможной степени с помощью системы типов Scala / Scala.js.

Если вам нужно использовать это часто, обертка, как предлагает @Thilo, действительно является вашим лучшим вариантом:

object RichRequest {
  def apply(uri: String, req: RequestConfig): Future[Response] = {
    val p = Promise[Response]()
    Request(uri, req, (error, response) => {
      if (error != null) p.failure(error)
      else p.success(response)
    })
    p.future
  }
}

В качестве альтернативы, если вы хотите сохранить обратный вызов API, рассмотрите возможность использования Try.

Обратите внимание, что если вы хотите пойти по этому пути, рассмотрите возможность использования request-обещание-native , где вы получите это из коробки (используя обещания JavaScript).

Так ваш фасад станет:

@js.native
@JSImport("request-promise-native", JSImport.Default)
object Request extends js.Object {
  def apply(uri: String, requestConfig: js.Object): js.Promise[Response] = js.native
}

А на сайте вызова:

Request(url, RequestConfig(...)).toFuture
...