0

I'm having trouble getting the syntax right for the Typescript React Props. I have an array of people each of which may have 0 - many cars. I have a people container that will contain all people, each person will have a car container that may contain cars, and car components that will be inside the car container.

I need to have the container for cars anyways because I will add edit buttons which will add cars to the person.

I'll add my code from top down:

PersonSlice:

export interface PersonState {
  id?: number;
  firstname?: string;
  lastname?: string;
  email?: string;
  created_at?: any;
  updated_at?: any;
  cars?: [];
}

People.tsx:

function People() {
  const people = useAppSelector(selectPerson);   //person is of type interface PersonState[]
  let contents;
    contents = <><div className="personcontainer">
      {people && people.length > 0 && people.map(person => {
        return <div key={person.id}>
          <Person
           dispatch={dispatch} 
           person={person}
           toggleEditForm={() => toggleEditForm(person.id)}
           personToEdit={personToEdit}
           submitEdit={submitEdit}
           />
          </div>
      })}
      </div></>
  });
}

This is where I start to have problems -

Person.tsx:

interface PersonProps {
  dispatch: Dispatch<any>;
  person: PersonState;
  toggleEditForm: () => void;
  submitEdit: any;
  personToEdit: number;
}

function Person(props: PersonProps) {
  return (
    <div className="person">
      <Cars cars={props.person.cars}/>  //cars error: type [] | undefined not assignable to car[]
    </div>
  );
}

cars.tsx:

import {car, Car} from './car';
interface cars {
    cars: car[];
}
function Cars (props: cars) {
    return (
        <div className="carcontainer">
            <h2>cars container</h2>
            {props.cars && props.cars.map((carobj: car) => {
                <Car car={carobj} key={}/>   //car error: Type '{ car: car; key: any; }' is not assignable to type 'IntrinsicAttributes & car'.
            })}
            </div>
    )
}
export default Cars;

and finally car.tsx:

export interface car {
    year: number,
    make:string,
    model: string,
    price: number,
    person_id: number,
}
export function Car (props: car) {
    return (
        <div className="carcontainer">
            <h3>
                {props.year} {props.make} {props.model} {props.price}
                </h3>
            </div>
    )
}

So I have two errors, one in person.tsx and one in cars.tsx which I added as comments in the code.

I've read like a dozen questions on this but I'm still super confused. What am I doing wrong?

Thanks

7
  • Why are all properties in your interface PersonState optional? That's part of the problem. Commented Mar 12, 2022 at 5:28
  • I will be sending updates to my api which may not contain all fields Commented Mar 12, 2022 at 5:29
  • 1
    In which case your interface PersonState should be renamed to interface PersonResponseDto and you should have a separate class PersonState for representing People in your Redux state store: DTOs should not be used directly for application state: class types are better because you can have ctor logic (which makes guarantees about validity), methods and properties - you can't do that with deserialized JSON objects. Commented Mar 12, 2022 at 5:30
  • Thanks for your reply but it kinda went over my head, I'm mostly just following a tutorial and is the first time I'm working with my own api. Could you recommend a resource to learn more about DTO and ctor? Commented Mar 12, 2022 at 5:38
  • 1
    The advice isn't TypeScript-specific: "DTOs" means "data transfer object": it's a general pattern (in many languages and systems) where data is passed around in a "POCO" object comprised of only properties/fields, without any methods (but can have (invariant) validation logic in their ctors); and "ctor" is a general abbreviation for constructor. In TypeScript/JSON specifically: when you use JSON.parse or {} then you're using POJO objects, not class objects, which means you can only use interface types containing only trivial properties (no methods, functions, ctors, etc). Commented Mar 12, 2022 at 5:45

2 Answers 2

1

The fix to both issues is in cars.tsx.

import {car, Car} from './car';
interface cars {
    cars?: car[]; // make this optional since the data from PeopleState is optional
}
function Cars (props: cars) {
    return (
        <div className="carcontainer">
            <h2>cars container</h2>
            {props.cars && props.cars.map((carobj: car) => {
                <Car {...carobj} key={}/>   // spread carobj into the component
            })}
            </div>
    )
}
export default Cars;
Sign up to request clarification or add additional context in comments.

Comments

0
  <Cars cars={props.person.cars}/>  //cars error: type [] | undefined not assignable to car[]

This isn’t really an error. You specifically wrote:

  cars?: [];

This means, “I might not know where or not the person has any cars.” If you don’t, obviously you cannot render the list of cars.

Maybe you really might not know, in which case, your code should be something like:

 { props.person.cars? 
    <Cars cars={props.person.cars}/> 
    : <span>I do not know if he has cars</span> }

Or, you really know about the cars, but you might know that there are no cars, in which case you should say so:

  cars: [];

In the second issue, you need to actually give it a key:

 {props.cars && props.cars.map((carobj: car, idx) =>
            <Car car={carobj} key={idx}/>   )}

1 Comment

Would you agree that people should default to readonly T[] for array-types as well? It works well in immutable contexts (like Redux/React) because only readonly T[] supports variance.

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.