2

So I've just started to combine react, redux (react-redux) and typescript. But I can't seem to figure out something here, namely, why the child components inside of the App component require all of the props that I pass into the App component.

App component:

const App = ({contacts, user, activeUserId}: ApplicationState) => (
    <div className="App">
        <Sidebar contacts={contacts} />
        <Main user={user} activeUserId={activeUserId}/>
    </div>
);

const getContacts = ({contacts, user, activeUserId}: ApplicationState) => ({contacts, user, activeUserId});
export default connect(getContacts)(App);

Sidebar component:

const Sidebar = ({contacts}: ApplicationState) => {
    let contactsValues: Array<Contacts> = contacts && Object.values(contacts);

    return (
        <aside className="Sidebar">
            {contactsValues && contactsValues.map((contact: Contacts) => <User user={contact} key={contact.user_id}/>)}
        </aside>
    )
}

I don't think there's any reason to list the Main component here as well since its only an empty component but gives the same errors, so I will just go ahead an list the Interfaces:

export default interface Contacts {
    email: string,
    name: string,
    profile_pic: string,
    status: string,
    user_id: string
}

export default interface ApplicationState {
    contacts: Contacts,
    user: object,
    activeUserId: string | null
}

Actual error code: "TS2739: Type '{ contacts: Contacts; }' is missing the following properties from type 'ApplicationState': user, activeUserId"

So, both components ask to have all the props injected into them, even though they are not required inside the component itself. If I supply the components the everything works and I get no errors.

I'm pretty sure there's something wrong with my syntax or 'wiring' of the types.

Can we also get a explanation or a decent link to docs/materials that help us understand why this is happening?

If there is anything else that I should be including please let me know and I will provide!

Edit:

I've actually realized that its important to show the Main component too because this one also causes problems and some of the fixes you guys have suggested only fix the Sidebar component but not the Main.

Main component:

import React from "react";
import "./Main.scss";

import ApplicationState from "../../models/ApplicationState";

const Main = ({user, activeUserId}: ApplicationState) => {
    return (
        <main className="Main">
            Main Stuff
        </main>
    )
}

Edit:

From what I've experimented with all of these answers I think the best solution would be to make everything in the interfaces optional, but if I do this then the contactsValues variable inside the Sidebar component throws: TS2322: Type 'any[] | undefined' is not assignable to type 'Contacts[]'.   Type 'undefined' is not assignable to type 'Contacts[]'..

3 Answers 3

1

The problem is with your Sidebar component where you just passed the contacts and defined its type to be ApplicationState... Change Sidebar parameter as:

const Sidebar = (contacts: Contacts) => { 
let contactsValues: Array<Contacts> = contacts && Object.values(contacts); 
return 
 ( <aside className="Sidebar"> 
   {contactsValues && contactsValues.map((contact: Contacts) =>
   <User user={contact} key={contact.user_id}/>
   )} 
  </aside> ) }

Another way to resolve the error is to make interface attribute optional as:

Update

I think there is one more problem with the interfaces:

export default interface Contact {   //It's a single object, So Contact is OK
 email: string,
 name: string,
 profile_pic: string,
 status: string,
 user_id: string
}
export default interface ApplicationState {
 contacts: Contact[],   // I guess you need an array of Contacts
 user?: object, 
 activeUserId?: string 
}

And I think you are getting error because of wrong initialization. Change to

let contactsValues: Array<Contacts> = contacts ? Object.values(contacts): [] as Contact[]; 
Sign up to request clarification or add additional context in comments.

3 Comments

Hello @MuhammadUmarFarooq, your first solution makes it so I can't use destructuring on the props object anymore and makes it so that we would have to use a lot of workarounds or more syntax. The second solution is a partial fix, it fixes the problem inside of the Sidebar component but not for the Main component.
Ok so, something you said got my attention in the right place, namely, the initialization, that was a big part of the problem, but not only that, it was also the interface types. Here is the answer: 1. All interface types in the ApplicationState must be optional since they are not required in all of the components, I still don't understand why they are all being required in all of the components simply because they are used to map the father component props but that seems to be how it works.
2. let contactsValues: Array<Contacts> | undefined = contacts && Object.values(contacts) - this seems to be the right way to initialize the variable, why? I think it is because the value inside this variable is being created using a prop that is optional and as such might not be passed in to the component! Makes sens right? If contacts? is optional the it might end up being undefined inside of Sidebar and as such contactsValues should have both types. I'm making your answer as the correct one because your answer helped me find out what was wrong. Thank you and happy coding!
1

The problem is in this function

const Sidebar = ({contacts}: ApplicationState) => {
    let contactsValues: Array<Contacts> = contacts && Object.values(contacts);

    return (
        <aside className="Sidebar">
            {contactsValues && contactsValues.map((contact: Contacts) => <User user={contact} key={contact.user_id}/>)}
        </aside>
    )
}

the type { contacts: Contacts } is not the same type ApplicationState

If you want to pass just contacts to your function you must create a new type or make ApplicationState like this


    export default interface ApplicationState {
        contacts: Contacts,
        user?: object,
        activeUserId?: string | null
    }


1 Comment

Hello @rubendamatos1985, if I try your solution the Sidebar will no longer throw a error but the Main component will still throw a error in reference to the fact that it is not getting the contacts property. Error: TS2741: Property 'contacts' is missing in type '{ user: object | undefined; activeUserId: string | null | undefined; }' but required in type 'ApplicationState'.
0

Replace the Applicationstate interface with this :

 export default interface ApplicationState {
   contacts?: Contacts,
   user?: object,
   activeUserId?: string | null
}

1 Comment

Hello @s.hesam, if I do this I get the following error in the Sidebar component: "TS2322: Type 'any[] | undefined' is not assignable to type 'Contacts[]'. Type 'undefined' is not assignable to type 'Contacts[]'." This error is present on let contactsValues: Array<Contacts> = contacts && Object.values(contacts);.

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.