2

I have a TypeScript question for you. I wonder how you 'specify' a single object in useState with the incoming properties of id, heading, text with an interface or such.

See code below:

import React, { useState, useEffect } from "react";

interface SinglePostProps {
  match: any;
}

interface SinglePostState {
  id: number;
  heading: string;
  text: string;
}

const Post: React.FC<SinglePostProps> = ({ match }) => {
  useEffect(() => {
    fetchSinglePost();
  }, []);

  const [singlePost, setSinglePost] = useState<SinglePostState>({});

  const fetchSinglePost = async () => {
    try {
      const fetchSinglePost = await fetch(
        `http://localhost:3001/api/posts/${match.params.id}`
      );
      const singlePost = await fetchSinglePost.json();
      console.log("Single Post", singlePost);
      setSinglePost(singlePost);
    } catch (error) {
      console.error(error);
    }
  };

  return (
    <>{/* <h1>{singlePost.heading}</h1>
      <p>{singlePost.text}</p> */}</>
  );
};

export default Post;

The error I get now is: enter image description here

If I console log the object that is saved as singlePost state I get the following message: enter image description here

As I understand it you want to specify if you're expecting an array for example or an object into the state. So when I did this is my PostList component which renders a list of objects inside of an array I did the following and it didn't complain:

const [items, setItems] = useState<PostListState[]>([]);

I'm quite new to TypeScript so it would be much appreciated if someone could explain how I can approach these kind of TypeScript error messages? :)

Thanks beforehand, Erik

erikos93
  • 677
  • 2
  • 12
  • 26
  • It looks like it expects you to provide an Object with all the properties defined in the `SinglePostState` interface since they're not [optional](https://www.typescriptlang.org/docs/handbook/interfaces.html#optional-properties). – Emile Bergeron Dec 09 '19 at 18:32
  • Does this answer your question? [Default property value in React component using TypeScript](https://stackoverflow.com/questions/37282159/default-property-value-in-react-component-using-typescript) – Emile Bergeron Dec 09 '19 at 18:35
  • Hmm maybe. So if I set the properties to optional it doesn't complain (obviously) but can you maybe tell me why that is? How should I think about it? The optional properties can be undefined? Do I always have to write them out as an Object inside of useState? – erikos93 Dec 09 '19 at 18:44
  • 1
    In your case, you probably want to make the state object optional, not the properties. So while the post is being fetched, you have `null` instead of `{}` and can check if the post is ready that way. I do not use TS but it's probably close to `/* ... */ = useState(null)` – Emile Bergeron Dec 09 '19 at 18:59

2 Answers2

2

As the error says, the setState function accepts an object which adheres to the SinglePostState interface or a function which returns such an object. You are seeing the error as you are sending an empty object literal which doesn't adhere to the SinglePostState interface whereas in the second case an empty array is also a valid array of PostListState elements. You can either send an empty array or an array where each element adheres to the PostListState interface. It would fail only if tried to send an array with other element types like an array of number or a completely different data type.

Ajay Ullal
  • 378
  • 2
  • 10
  • This provides an accurate description of the error, but it doesn't provide any solution. – Emile Bergeron Dec 09 '19 at 18:55
  • I think the solution is straightforward, you can either instantiate an empty object of type SinglePostState and then pass that object to the setState function or you can pass any generic parameter useState({}); – Ajay Ullal Dec 09 '19 at 19:01
1

You are telling the compiler that every PostListState Object must have their params if you want to have optional params you just need to add ? after the key.

http://www.typescriptlang.org/docs/handbook/advanced-types.html#optional-parameters-and-properties


interface SinglePostStateOPTIONAL {
  id?: number;
  heading?: string;
  text?: string;
}


interface SinglePostState {
  id: number;
  heading: string;
  text: string;
}

//useState<SinglePostState>(/* only works with {id:0, heading:'', text:''}*/);
//useState<SinglePostStateOPTIONAL>(/* works with both {} or {id:0, heading:'', text:''}*/);

//This will fail
const [items, setItems] = useState<PostListState>({});

//This will be right 
const [items, setItems] = useState<PostListState>({id: 0, heading:'', text:''});

//This also will be right

const [items, setItems] = useState<PostListStateOPTIONAL>({});



When you are using arrays you can do the follow:


const [items, setItems] = useState<PostListState[]>([]);

you don't have any problem with the array of PostListState Objects because the length of the array is equal to 0, so that means that you can have empty array of PostListState.

//You can have this
const [items, setItems] = useState<PostListState[]>([]); 

// This will fail
const [items, setItems] = useState<PostListState[]>([{}]); 

// This will be right
const [items, setItems] = useState<PostListState[]>([{id: 0, heading:'', text:''}]); 

// This will fail too because you need to pass all the params of the object.
const [items, setItems] = useState<PostListState[]>([{id: 0, heading:''}]);