#reactjs #typescript
#reactjs #typescript
Вопрос:
Я рефакторингую свое приложение React на Typescript и пока все хорошо. Я работаю над своим последним компонентом, тем, который извлекает данные API, и я пытаюсь понять, почему эти ошибки типа не разрешаются. Я использовал https://jvilk.com/MakeTypes / чтобы помочь правильно настроить интерфейсы.
Object is possibly 'null'
дважды для error
amp; stories
(прокомментированные строки ниже)
Parameter '___' implicitly has an 'any' type.
дважды для story
amp; idx
(прокомментированные строки ниже)
У меня возникли проблемы с этим компонентом вызова API. Чего мне не хватает?
import React, { FC, ReactElement, useEffect, useState } from "react";
import Story from "./Story";
export interface NewsProps {
results?: (ResultsEntity)[] | null;
}
export interface ResultsEntity {
section: string;
title: string;
abstract: string;
url: string;
multimedia?: (MultimediaEntity)[] | null;
}
export interface MultimediaEntity {
url: string;
caption: string;
}
const News: FC<NewsProps> = ({results:ResultsEntity}):ReactElement => {
const [error, setError] = useState(null);
const [stories, setStory] = useState(null);
useEffect(() => {
const getCurrentPage = () => {
const url = new URL(window.location.href);
const page = url.pathname.split("/").pop();
return page ? page : "home";
};
const section = getCurrentPage();
fetch(
`https://api.nytimes.com/svc/topstories/v2/${section}.json?api-key=4fzCTy6buRI5xtOkZzqo4FfEkzUVAJdr`
)
.then((res) => res.json())
.then((data) => {
setTimeout(() => setStory(data), 1500);
})
.catch((error) => {
console.log("Error", error);
setError(error);
});
}, []);
if (error) {
return <div>Error: {error.message}</div>; /// "Object is possibly 'null'" (for error)
} else
if (!stories) {
return <div>Loading...</div>
} else {
return (
<>
<ul className="stories">
{stories.results.map((story, idx) => { // "Object is possibly 'null'" (for stories)
// Parameter '__' implicitly has an 'any' type (for idx amp; story)
return (
<Story
key={idx}
title={story.title}
abstract={story.abstract}
img={
story amp;amp;
story.multimedia amp;amp;
story.multimedia[0] amp;amp;
story.multimedia[0].url
? story.multimedia[0].url
: null
}
alt={
story amp;amp;
story.multimedia amp;amp;
story.multimedia[0] amp;amp;
story.multimedia[0].caption
? story.multimedia[0].caption
: null
}
link={story.url}
/>
);
})}
</ul>
</>
);
}
}
export default News;
Ответ №1:
Typescript не понимает тип null
const [error, setError] = useState<string>(null);
Ответ №2:
В вашем коде есть два места, где компилятор TS не знает точных типов некоторых значений и предупреждает вас, что вы пытались сделать (возможно) недопустимые вещи с ними.
Добавление | null
также не помогает, поскольку это не причина проблемы, и (если вы не используете --strictNullChecks
флаг) null
по умолчанию присваивается любому типу, поэтому вводить его не нужно.
Проблемные моменты:
error.message
Хотя вы проверяете правдивость
error
, компилятор не может сделать вывод, что этого не может бытьnull
. И, конечно, обращение к свойствуnull
было бы ошибкой, отсюда и предупреждение.Чтобы решить эту проблему, вы можете использовать оператор утверждения, отличный от null:
error!.message
stories
Поскольку это значение задается динамически из сетевого запроса, TS не будет знать его тип (он не может вывести его из значения по умолчанию, как оно
null
есть ). Это приводит к тому, что он естьany
, и операции над ним также неизвестны компилятору (например, он не знает, что.map()
этоArray#map()
так, поэтому также не знает типы аргументов, которые он передаст своему обратному вызову).Чтобы исправить это, вам нужно будет определить точный тип (интерфейс)
stories
(и его свойства, рекурсивно). Затем передайте этот интерфейсuseState
, чтобы определить тип переменной состояния.Однако, как
stories
иnull
иногда, после правильного определения его типа вам придется использовать ненулевое утверждение перед доступом к свойству по тем же соображениям, что и выше.
Подводя итог, ваш код должен выглядеть следующим образом:
import React, { FC, ReactElement, useEffect, useState } from "react";
import Story from "./Story";
export interface NewsProps{
//Props of the component go here
//Don't confuse them with state or other variables
}
interface Result {
results: ResultsEntity[];
}
interface ResultsEntity {
section: string;
title: string;
abstract: string;
url: string;
multimedia?: MultimediaEntity[];
}
interface MultimediaEntity {
url: string;
caption: string;
}
const News:FC<NewsProps> = ():ReactElement => {
const [error, setError] = useState<Error | null>(null);
const [stories, setStory] = useState<Result | null>(null);
useEffect(() => {
const getCurrentPage = () => {
const url = new URL(window.location.href);
const page = url.pathname.split("/").pop();
return page ? page : "home";
};
const section = getCurrentPage();
fetch(
`https://api.nytimes.com/svc/topstories/v2/${section}.json?api-key=4fzCTy6buRI5xtOkZzqo4FfEkzUVAJdr`
)
.then((res) => res.json())
.then((data) => {
setTimeout(() => setStory(data), 1500);
})
.catch((error) => {
console.log("Error", error);
setError(error);
});
}, []);
if (error) {
// v-- Non-null assertion (it can't be null, you've checked beforehand)
return <div>Error: {error!.message}</div>;
} else
if (!stories) {
return <div>Loading...</div>
} else {
return (
<>
<ul className="stories">
// v-- Non-null assertion (it can't be null, you've checked beforehand)
{stories!.results.map((story, idx) => {
return (
<Story
key={idx}
title={story.title}
abstract={story.abstract}
img={
// vv-- It's enough to do this (optional chaining, new JS feature). Much more readable. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining
story.multimedia?.[0].url ?? null
// ^^^^^^^^^^^ ^^^^-- Fallback value
// -- Thing that may be null or undefined
}
alt={
story.multimedia?.[0].caption ?? null //Same as above
}
link={story.url}
/>
);
})}
</ul>
</>
);
}
}
export default News;
Посмотрите его в режиме реального времени на игровой площадке TS (он не выдает ошибок)
Комментарии:
1. Хорошо, я обновил свой код и добавил больше типов, если это поможет отобразить все возвращаемые данные. Я все еще получаю те же ошибки.
2. @Christian Я включил пример кода. Проверьте это, оно прокомментировано и не выдает ошибок TS. Надеюсь, теперь все стало понятнее.