skip to Main Content

I am a complete beginner with React but fairly comfortable with vanilla JS, and have been following a YouTube tutorial. The tutorial did this part a little differently hence the need for help.

Basically I am setting an ‘interface ListProps’ in my ListGroup component file. But in my App.tsx I am getting 2 errors. Though I am getting these errors, the application still works as I intended, but I don’t know why I’m getting these errors or how write the code better to avoid them.

##components/ListGroup.tsx:

import { useState } from "react";

interface ListProps {
    items: string[];
    heading: string;
}

function ListGroup({ items, heading }: ListProps) {
    // this controls the state of an element.
    const [selectedIndex, setSelectedIndex] = useState(-1);

    const handleClick = (index: number) => {
        // set the selectedIndex (which adds the 'active' class to an element in my className declaration)
        // equal to the index of the clicked item, updating appearance.
        setSelectedIndex(index);
    };

    const noItem = () => {
        return (
            // true && x
            //will return x
            // false && x
            //will return false
            items.length === 0 && (
                <div className="card">
                    <div className="card-body bg-danger text-white">
                        No results found.
                    </div>
                </div>
            )
        );
    };

    return (
        <>
            <h1>{heading}</h1>
            {noItem()}
            <ul className="list-group">
                {/* no for loops in jsx, so instead we map the items and turn them into
        html elements */}
                {items.map((item, index) => (
                    <li
                        className={
                            selectedIndex === index
                                ? "list-group-item active"
                                : "list-group-item"
                        }
                        key={item}
                        onClick={() => handleClick(index)}
                    >
                        {item}
                    </li>
                ))}
            </ul>
        </>
    );
}

export default ListGroup;

##App.tsx:

import ListGroup from "./components/ListGroup";

function App() {
    let items = ["Cities", ["New York", "London", "Tokyo", "Paris"]];

    return (
        <div>
            <ListGroup items={items[1]} heading={items[0]} />
        </div>
    );
}

export default App;

As you can see from the above code, I am trying to streamline my code by not having separate containers for the heading and the array, so I have put the array of cities inside another array, whose first value is the heading I want to use. The tutorial has declared them manually like this:

function App() {
    let items = ["New York", "London", "Tokyo", "Paris"];

    return (
        <div>
            <ListGroup items={items} heading="Cities" />
        </div>
    );
}

But I want to make it more dynamic than that. I don’t want to have to update the heading manually if I have a different list of items to display.

As mentioned, the code I have written works and displays everything the way I want it, but I get the following errors from App.tsx:

Type 'string | string[]' is not assignable to type 'string[]' [Ln 9, Col 24].
  Type 'string' is not assignable to type 'string[]'.
Type 'string | string[]' is not assignable to type 'string' [Ln 9, Col 41].
  Type 'string[]' is not assignable to type 'string'.

I have tried switching the interface declaration to:

interface ListProps {
    items: string;
    heading: string;
}

Doesn’t work, just flips the error to say this instead:

Type 'string | string[]' is not assignable to type 'string[]' [Ln 9, Col 24].
  Type 'string' is not assignable to type 'string'.

Edit: There are no console errors in the browser

3

Answers


  1. The heading property is that it complains about, not the items.
    You set to the heading a value ["New York", "London", "Tokyo", "Paris"] while it expects a single string.

    Login or Signup to reply.
  2. Typescript expects Arrays to be homegenous as they should be. What that means is that Arrays should and can have items of the same type only.

    So basically if you have an array:

    const arrayNum = ["1",2,3,4]
    

    This array will actually have the type of string[] instead of number[] because the first item is a string.

    This is similar to your case. You have a string in first item and an array in the second. It expects the second item to also be a string instead of an array of string.

    The correct approach to this would be to replace the array with an object, Which can be heterogenous.

    const config = {
    heading: "Cities",
    item: ["New York", "London", "Tokyo", "Paris"],
    }
    

    This is more readable and it will work also as you expect.

    Note: Always try to create a sandbox of your code, so people can better help you debug.

    Login or Signup to reply.
  3. Add an explicit type annotation to items. Per default TypeScript infers items as (string | string[])[] but you want it to be more specific than that.

    let items: [string, string[]] = [
      "Cities",
      ["New York", "London", "Tokyo", "Paris"]
    ];
    const heading = items[0]; // string
    const itemsProp = items[1]; // string[]
    

    Now everything is assignable.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search