打字稿/反应:“对象可能是‘空’”

时间:2021-02-27 16:24:40

标签: reactjs typescript fetch react-typescript

我正在将 React 组件更改为 Typescript。它调用一个 API。我相信我的接口设置正确,但我没有正确传递类型。

如何修复下面父级中的两个错误?


  • return <div>Error: {error.message}</div>; // "Object is possibly 'null'"
  • {stories.results.map((story, idx) => { // "Object is possibly 'null' or 'undefined'"
  • alt={ // Type 'string | null' is not assignable to type 'string | undefined'. Type 'null' is not assignable to type 'string | undefined'.

我的界面


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;
  alt:string;
}

父组件


import React, { FC, ReactElement, useEffect, useState } from "react";
import Story from "./Story";

const News: FC<NewsProps> = ({results:ResultsEntity}):ReactElement => {
  const [error, setError] = useState(null);
  const [stories, setStory] = useState<NewsProps>();

  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) => {
        setError(error);
      });
  }, []);

  if (error) {
    return <div>Error: {error.message}</div>; // "Object is possibly 'null'"
  } else 
  if (!stories) {
    return <div>Loading...</div>
  } else {
    return (
      <>
        <ul className="stories">
          {stories.results.map((story, idx) => {  // "Object is possibly 'null' or 'undefined'"
            return (
              <Story
                key={idx}
                title={story.title}
                abstract={story.abstract}
                img={story.multimedia[0].url}
                alt={  // Type 'string | null' is not assignable to type 'string | undefined'. Type 'null' is not assignable to type 'string | undefined'.
                  story &&
                  story.multimedia &&
                  story.multimedia[0] &&
                  story.multimedia[0].caption
                    ? story.multimedia[0].caption
                    : null
                }
                link={story.url}
              />
            );
          })}
        </ul>
      </>
    );
  }
}
export default News;

孩子


import React, { FC, ReactElement } from "react";

interface StoryProps {
  title?: String;
  img?: string;
  alt?: string;
  abstract?: string;
  link?: string;
}

const Story: FC<StoryProps> = ({title, img, alt, abstract, link}): ReactElement => {
  return (
    <div className="story">
      <li className="story-title">{title}</li>
      <span className="story-content">
        <img className="story-img" src={img} alt={alt} />
        <span>
          <li className="story-body">{abstract}</li>
        </span>
      </span>
    </div>
  );
};
export default Story;

2 个答案:

答案 0 :(得分:2)

对于error,问题是当你创建状态时,你没有明确地给它一个类型,所以打字稿必须尝试推断类型。它看到你传入了 null,所以它假设状态是(并且永远是)null:

const [error, setError] = useState(null);

如果你知道错误是什么,你可以给出一个代表它的类型。例如:

const [error, setError] = useState<null | { message: string }>(null);

如果你什么都不知道,那么你可能需要求助于any

const [error, setError] = useState<any>(null);

对于 stories,您已将 .results 属性定义为可能为 null 或未定义:

export interface NewsProps {
  results?: (ResultsEntity)[] | null;
}

...但是您在使用之前不会检查这些值。您需要编写代码来检查这些情况。例如,如果您只想跳过 map 语句并且不渲染任何内容,您可以这样做:

{stories.results && stories.results.map((story, idx) => {

或者与可选链相同的东西(需要 typescript 3.7 或更高版本):

{stories.results?.map(story, idx) => {

对于 alt 属性,您传入的是 null,但这不是 alt 的合法值。改为传入 undefined

alt={
  story &&
  story.multimedia &&
  story.multimedia[0] &&
  story.multimedia[0].caption
    ? story.multimedia[0].caption
    : undefined
}

或者使用可选的链接:

alt={story?.multimedia?.[0]?.caption}

答案 1 :(得分:0)

对于这两种情况,您都可以使用“?”运算符(可选链)。

 <div>Error: {error?.message}</div>;

 // This is similar to ( check if error is present then access the property on it )
 <div>Error: {error && error.message}</div>;
<ul className="stories">
      {stories.results?.map((story, idx) => {
        return (
          <Story
            key={idx}
            title={story.title}
            abstract={story.abstract}
            img={story.multimedia[0].url}
            alt={story.multimedia[0].caption}
            link={story.url}
          />
        );
      })}
    </ul>

注意 - 在第二种情况下,我输入“?”在结果前面,原因是在您的界面中“NewsProps”结果属性可以是未定义的或空的。

参考:https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#optional-chaining

-- 修改后--

  • 您的 alt 问题即将出现,因为在 Story 组件上,您希望 alt 的类型为“字符串”或“未定义”(您添加了?它使属性选项成为 undefined )。但是您在所有来自父项的检查结束时传递“null”,因此它会给您一个类型错误,修复它的一种方法是使用空字符串“”而不是 null。
interface StoryProps {
  title?: String;
  img?: string;
  alt?: string; // -> This is the one string | undefined
  abstract?: string;
  link?: string;
}

 <Story
   key={idx}
   title={story.title}
   abstract={story.abstract}
   img={story.multimedia[0].url}
   alt={ 
    story &&
    story.multimedia && 
    story.multimedia[0] &&
    story.multimedia[0].caption 
    ? story.multimedia[0].caption 
    : null // -> here use "" instead of null
   }
    link={story.url}
 />