-1

I have an nested object in React functional component state, but I only can access first level of object if I use any type.

export default function Detail() {
    const [user, setUser] = useState<any>({});
    const { id } = useParams();


    useEffect(() => {
        fetch('http://localhost:4000/users/' + id)
        .then(response => {
            return response.json();
        })
        .then(data => {
            setUser(data);
        })
    }, []);
    
    

    return (
        <div>
            <h1>Detail view</h1>
            <p>ID: { user.id }</p>
            <p>First name: { user.first_name }</p>
            <p>Last name: { user.last_name }</p>
            <p>Email: { user.email }</p>
            <p>Gender: { user.gender }</p>
        </div>
    );
}

When trying to access user.company.name it throws

Detail.tsx:40 Uncaught TypeError: Cannot read properties of undefined (reading 'name')

I made an interface for it, but it gives me an error if I'm trying to use it for state type.

interface UserInfo {
    id: number;
    first_name: string;
    last_name: string;
    email: string;
    gender: string;
    company: {
        name: string;
        department: string;
    }
}
Argument of type '{}' is not assignable to parameter of type 'UserInfo | (() => UserInfo)'.

How can I use defined interface as state type?

2
  • A quick fix is to use {user.company && <p>{user.company.name}</p>} to prevent the error. Not a typescript expert, but you probably need to assign an intial object that already has data for the interface properties. See also here: stackoverflow.com/questions/36745013/… Commented Feb 13, 2022 at 10:26
  • also there is optional chaining. not sure if possible in Ts Commented Feb 13, 2022 at 10:30

1 Answer 1

3

Don't use the any type. TypeScript can't help you if you don't use actual types. Type the state narrowly.

Don't use an empty object as your default value. It isn't useful. Use a value that clearly indicates that the data isn't available yet.


type PossibleUserInfo = UserInfo | null;

const [user, setUser] = useState<PossibleUserInfo>(null);

Then, handle the case where you don't have the data yet explicitly.

It isn't useful to spit out a div with a bunch of paragraphs containing a label but not data.


if (user) {
    return (
        <div>
            <h1>Detail view</h1>
            <p>ID: { user.id }</p>
            <p>First name: { user.first_name }</p>
            <p>Last name: { user.last_name }</p>
            <p>Email: { user.email }</p>
            <p>Gender: { user.gender }</p>
        </div>
    );
}

return (
    <div>
        <h1>Default view</h1>
        <Loading />
    </div>
);

… where Loading is a component that shows (for example) a loading spinner.

Sign up to request clarification or add additional context in comments.

1 Comment

I tried UserInfo | null before but it didn't worked, because I didn't used conditional statement on return. Now it works. Thanks

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.