Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript/React: "Object is possibly 'null'"

I'm changing a React component to Typescript. It calls an API. I believe my interfaces are set up correctly, but I'm not passing the types correctly.

How do I fix the two errors in the Parent below?


  • 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'.

My Interfaces


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;
}

Parent Component


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;

Child


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;
like image 211
cdt Avatar asked Sep 05 '25 03:09

cdt


1 Answers

For error, the issue is that when you create the state, you didn't explicitly give it a type, so typescript has to try to infer the type. It sees you passed in null, so it assumes the state is (and always will be) null:

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

If you know something about what the error will be, you can give a type that represents that. For example:

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

If you know nothing, then you may need to resort to any:

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

For stories, you've defined the .results property as being possibly null or undefined:

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

...but then you don't check for those values before using it. You'll need to write code to check for those cases. For example, if you just want to skip the map statement and render nothing you can do this:

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

Or the same thing with optional chaining (requires typescript 3.7 or later):

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

For the alt prop, you're passing in null, but that's not a legal value for alt. Pass in undefined instead.

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

Or with optional chaining:

alt={story?.multimedia?.[0]?.caption}
like image 141
Nicholas Tower Avatar answered Sep 07 '25 22:09

Nicholas Tower