Как использовать тип Js.Dict.t для Js.Dict.get

Я пытаюсь преобразовать JS в Reason, попутно мне нужно ввести ответ JSON, а также проверить, существует ли ключ в объекте.

Это мой текущий код:

let api_key = "";
let api_url = "http://ws.audioscrobbler.com/2.0";
let method = "user.getRecentTracks";
let user = "montogeek";

type trackAttr = {
  nowplaying: bool
};

type artistT = {
  text: string
}

type trackT = {
  attr: trackAttr,
  name: string,
  artist: artistT
};

type recentTrackT = {
  track: array(Js.Dict.t(trackT))
};

type response = {
  recenttracks: recentTrackT
};

Js.Promise.(Fetch.fetch(api_url ++ "?method=" ++ method ++ "&" ++ user ++ "=" ++ user ++ "&limit=1&format=json&api_key=" ++ api_key)
    |> then_(Fetch.Response.json)
    |> then_(json: response => {
      let lasttrack = json.recenttracks.track[0];

      let online = switch (Js.Dict.get(lasttrack, "attr")) {
      | None => false
      | Some(track) => track.attr.nowplaying
      };
      let info = online ? "Enjoying" ++ lasttrack.name ++ "by " ++ lasttrack.artist["#text"] ++ "}" : "";

      { online, info }
    }));

В настоящее время я получаю эту ошибку:

We've found a bug for you!
  /Users/montogeek/Infinite/Lov/online/src/lastfm.re 37:41-49

  35 ┆ | Some(track) => track.attr.nowplaying
  36 ┆ };
  37 ┆ let info = online ? "Enjoying" ++ lasttrack.name ++ "by " ++ lasttrack
       .artist["#text"] ++ "}" : "";
  38 ┆
  39 ┆ { online, info }

  This has type:
    Js.Dict.t(trackT) (defined as Js.Dict.t(trackT))
  But somewhere wanted:
    trackT

Я не могу удалить тип Js.Dict.t, потому что Js.Dict.get он не понравится.

Как я могу ввести ответ, чтобы он работал?

Спасибо!


person Fernando Montoya    schedule 23.05.2018    source источник


Ответы (1)


Возможно, вы захотите попробовать построить это меньшими шагами, так как вы копали здесь довольно глубокую яму.

Прежде всего, вы описываете свой ответ JSON в терминах типов записей, а не типов объектов JS.

Это тип записи:

type  t = { foo: string };

и это тип объекта JS:

type t = {. "foo": string };

Тонкая, но довольно существенная разница.

Во-вторых, Js.Dict.t(trackT) - это не то, что вы думаете (хотя мне тоже непонятно, что именно). Он описывает объект JS, используемый как хэш, с ключами типа String и значениями типа trackT. Это похоже на попытку исправить первую проблему, но служит только для того, чтобы сделать дыру глубже, так что, возможно, просто исправьте это.

В-третьих, вы не можете просто утверждать, что ответ JSON имеет тип response, аннотируя его. Это система типов звука, в которой вы получите ошибку типа. Если вы хотите обойти систему типов (что почти всегда является плохой идеей), вы должны быть более точными.

Также есть ряд других мелких ошибок, но это выходит за рамки нашего рассмотрения.

Вместо этого вам следует сделать одно из двух:

Правильный способ - использовать такую ​​библиотеку, как bs-json., чтобы декодировать ответ JSON в типы записей, которые вы уже определили, проверяя форму данных на границе, где это должно быть сделано, а затем продолжайте использовать типы записей как обычно:

/* assume record types are defined above */

module Decode = {
  open Json.Decode;

  let trackAttr = json => {
    nowplaying: json |> field("nowplaying", bool)
  };

  let artist = json => {
    text: json |> field("text", string)
  }

  let track = json => {
    attr: json |> optional(field("attr", trackAttr)),
    name: json |> field("name", string),
    artist: json |> field("artist", artist)
  };

  let recentTrack = json => {
    track: json |> field("track", array(track))
  };

  let response = json => {
    recenttracks: json |> field("recenttracks", recentTrack)
  };
};

Js.Promise.(Fetch.fetch(api_url ++ "?method=" ++ method ++ "&" ++ user ++ "=" ++ user ++ "&limit=1&format=json&api_key=" ++ api_key)
    |> then_(Fetch.Response.json)
    |> then_(json => {
      let response = Decode.response(json);
      let lasttrack = response.recenttracks.track[0];
      ...
    }));

Быстрый и грязный способ - преобразовать типы записей в типы объектов JS, а затем просто подтвердить, что данные имеют эту форму, тем самым обойдя систему типов. Если он не имеет той формы, которую вы ожидаете, вы можете получить ошибки времени выполнения где угодно, и вам нужно будет вручную отследить источник проблемы. Вот как это может выглядеть:

type trackAttr = {.
  "nowplaying": bool
};

type artistT = {.
  "text": string
}

type trackT = {.
  "attr": trackAttr,
  "name": string,
  "artist": artistT
};

type recentTrackT = {.
  "track": array(trackT)
};

type response = {.
  "recenttracks": recentTrackT
};

external unsafeCastJsonAsResponse : Js.Json.t => response = "%identity";

Js.Promise.(Fetch.fetch(api_url ++ "?method=" ++ method ++ "&" ++ user ++ "=" ++ user ++ "&limit=1&format=json&api_key=" ++ api_key)
    |> then_(Fetch.Response.json)
    |> then_(json => {
      let response = unsafeCastJsonAsResponse(json);
      let lasttrack = response##recenttracks##track[0];
      ...
    }));
person glennsl    schedule 23.05.2018