Как правильно набрать проанализированные JSON данные в TypeScript и сузить их для известных данных ответа? - PullRequest
0 голосов
/ 13 февраля 2020

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

Я хочу:

  • создать тип, который (вместо использования any) охватывает все возможные значения, которые могут быть возвращены из Body.json(), чтобы использовать его в типе возврата для многократно используемой функции fetchJson, а затем
  • сузить этот тип только до формы известных данных, поступающих из API

Чтобы проиллюстрировать это, я подготовил пример, используя конечную точку xkcd JSON . После некоторого чтения и настройки, я пришел к рабочей версии, которая не дает ошибок компиляции / линтинга, но я не совсем понимаю, почему в предыдущих вариантах кода возникают определенные ошибки, и надеюсь, что кто-то сможет дать подробное объяснение, почему является. Я пометил комментариями строки, по которым у меня есть вопросы, и перечислю особенности вопроса и ошибки после примера.

Пример:

// ./types/json.ts

export type JsonPrimitive = boolean | null | number | string;
export type JsonArray = Array<JsonData>;
export type JsonObject = { [prop: string]: JsonData };
export type JsonData = JsonArray | JsonObject | JsonPrimitive;
// xkcd.ts

import {JsonData, JsonObject} from './types/json';
import fetch from 'node-fetch';

interface XkcdComicData extends JsonObject { // First line
  alt: string;
  day: string;
  img: string;
  link: string;
  month: string;
  num: number;
  news: string;
  safe_title: string;
  title: string;
  transcript: string;
  year: string;
}

const fetchJson = async (url: string): Promise<JsonData | never> => {
  const response = await fetch(url);
  if (!response.ok) {
    throw Object.assign(new Error(), {
      message: 'Fetch response not OK',
      name: 'FetchError',
      response,
    });
  }
  return response.json();
};

const getComicEndpoint = (comicNumber?: number): string => {
  let comic = '';
  if (comicNumber !== undefined) comic += `${comicNumber}/`;
  const url = new URL(`https://xkcd.com/${comic}info.0.json`);
  return url.toString();
};

const fetchComic = (comicNumber?: number): Promise<XkcdComicData | never> =>
  fetchJson(getComicEndpoint(comicNumber)) as Promise<XkcdComicData>; // Second line

export {fetchComic, fetchComic as default};
// logLatestComic.ts

import {fetchComic} from './xkcd.js';

const logLatestComic = async (): Promise<void> => {
  console.log(await fetchComic());
};

logLatestComic();

Вариант кода 1:

Если я изменяю первую строку на

interface XkcdComicData extends JsonData {

, я получаю эту ошибку:

Интерфейс может только расширять тип объекта или пересечение объекта типы со статически известными членами. ts (2312)

Но я не знаю, что здесь подразумевается под "типами объектов со статически известными членами", поскольку расширение JsonData (которое использует сигнатуру строкового индекса) не представляет проблемы.

Внесение вышеуказанного изменения (или удаление extends JsonObject из первой строки) также приводит к ошибке во второй строке:

Преобразование типа 'Promise ' в тип «Обещание » может быть ошибкой, поскольку ни один из типов не совпадает с другим. Если это было сделано намеренно, сначала преобразуйте выражение в «unknown».
Тип «JsonData» не сопоставим с типом «XkcdComicData».
Тип «JsonObject» не содержит следующие свойства из типа «XkcdComicData»: alt, день, img, ссылка и еще 7. ts (2352)

Вариант кода 2:

Используя любую из трех предыдущих форм первой строки, если я удалю утверждение типа из второй строки, например, так:

fetchJson(getComicEndpoint(comicNumber));

Я получаю эту ошибку:

Тип 'Promise ' нельзя назначить типу 'Promise '.
Тип 'JsonData' не является присваивается типу 'XkcdComicData'.
Тип 'null' не присваивается типу 'XkcdComicData'. ts (2322)

Вопросы

  1. Есть ли способ избежать использования утверждения типа при сохранении текущей структуры кода?

  2. Я понимаю ошибку, представленную в варианте кода 2, описанном выше, но я не понимаю ошибки в варианте 1. Надеюсь, вы понимаете мое намерение - я хотел бы объяснить, как достичь своей цели и почему мои попытки не дали того, что я хотел.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...