0

Grettings.

So I am trying typescript. the initial code which I based on is on javascript

const PokemonTable = () => {
  const pokemon = useStore((state) => state.pokemon);
  const filter = useStore((state) => state.filter);

  return (
    <table width="100%">
      <tbody>
        {pokemon
          .filter(({ name: { english } }) =>
            english.toLowerCase().includes(filter.toLowerCase())
          )
          .map(({ id, name: { english }, type }) => (
            <tr key={id}>
              <td>{english}</td>
              <td>{type.join(", ")}</td>
            </tr>
          ))}
      </tbody>
    </table>
  );
};

When I change it into typescript it throws these errors

Error

I tried to create an interface to pass the props to it

//interfaz de pokemon table
interface PkTableProps{
  id:number,
  name:Array<String>,
  type:Array<String>,
}

And then I applied to the arrow function

// pokemon table

const PokemonTable = (props : PkTableProps) => {
  const pokemon = useStore((state) => state.pokemon);
  const filter = useStore((state) => state.filter);

  return (
    <table width="100%">
      <tbody>
        {pokemon
          .filter(({ props.name : { english } }) =>
            english.toLowerCase().includes(filter.toLowerCase())
          )
          .map(({ id, props.name: { english }, props.type }) => (
            <tr key={props.id}>
              <td>{english}</td>
              <td>{props.type.join(", ")}</td>
            </tr>
          ))}
      </tbody>
    </table>
  );
};

And I get these new syntax errors

Error

NOTE

In the line <td>{english}</td> on the table of the arrow function. I am doing it like that because I am trying to chose the name of each pokemon in english language. That means I want it as key

Here is the structure of the array of pokemons that I am using
https://gist.githubusercontent.com/jherr/23ae3f96cf5ac341c98cd9aa164d2fe3/raw/0658aeff401d196dece7ec6fe6c726c6adc1cc00/gistfile1.txt

This is the complete code

import React from "react";
import "./App.css";
import create from "zustand";
// import { mountStoreDevtool } from 'simple-zustand-devtools';

const POKEMON_URL =
  "https://gist.githubusercontent.com/jherr/23ae3f96cf5ac341c98cd9aa164d2fe3/raw/f8d792f5b2cf97eaaf9f0c2119918f333e348823/pokemon.json";

// definir el tipo de mi store
type State = {
  filter: string;
  pokemon: Array<string>;
  setFilter: (filter: string) => void;
  setPokemon: (pokemon: Array<string>) => void;
};

//interfaz de pokemon table
interface PkTableProps{
  id:number,
  name:Array<String>,
  type:Array<String>,
}


//tienda de estados
const useStore = create<State>((set) => ({
  // set initial values here
  filter: "",
  pokemon: [],

  setFilter: (filter: string) =>
    set((state) => ({
      ...state,
      filter,
    })),

  setPokemon: (pokemon: Array<string>) =>
    set((state) => ({
      ...state,
      pokemon,
    })),
}));

// if (process.env.NODE_ENV === 'development') {
//   mountStoreDevtool('Store', store);
// }

//  input
const FilterInput = () => {
  const filter = useStore((state) => state.filter);
  const setFilter = useStore((state) => state.setFilter);
  return (
    <input value={filter} onChange={(evt) => setFilter(evt.target.value)} />
  );
};

// pokemon table

const PokemonTable = (props : PkTableProps) => {
  const pokemon = useStore((state) => state.pokemon);
  const filter = useStore((state) => state.filter);

  return (
    <table width="100%">
      <tbody>
        {pokemon
          .filter(({ props.name : { english } }) =>
            english.toLowerCase().includes(filter.toLowerCase())
          )
          .map(({ id, props.name: { english }, props.type }) => (
            <tr key={props.id}>
              <td>{english}</td>
              <td>{props.type.join(", ")}</td>
            </tr>
          ))}
      </tbody>
    </table>
  );
};


function App()  {
  const setPokemon = useStore((state) => state.setPokemon);
  

  //lifecycle hook every time the component renders

  React.useEffect(() => {
    fetch(POKEMON_URL)
      .then((resp) => resp.json())
      .then((pokemon) => setPokemon(pokemon));
  });

  return (
    <div className="App">
      <div>
        <FilterInput />
      
      </div>
      <h1>List of Pokemons</h1>
      <PokemonTable />
      {/* mostrar el resultado de filter */}
      {/* {filter}   */}
      {/* mostar lo que el contenido del array de la url */}
      {/* {JSON.stringify(pokemon)} */}
    </div>
  );
}

export default App;
Karol
  • 166
  • 1
  • 4
  • 15
  • You've declared `pokemon` as `Array`, which it most certainly isn't. It's an `Array` for some appropriate definition of `PokemonEntry` as an object. JS doesn't complain because it has no concept of types, but for TS you must declare the type correctly. – Niet the Dark Absol May 22 '21 at 23:51
  • @NiettheDarkAbsol I changed it like this `pokemon: Array;`. It throws this error - `Cannot find name PokemonEntry`-. Should I Create this new element as an `Array` ? – Karol May 23 '21 at 13:31
  • No. You need to declare what it is correctly. Looking at your code it seems you want `Array`, but even that is declared incorrectly. `name` should be `{[lang:string]: string}` and `type` ideally should be an array of enum entries. Furthermore it's missing `base` altogether, which should be itself an object with the appropriate keys mapped to `number`s. – Niet the Dark Absol May 23 '21 at 13:59

1 Answers1

0
type State = {
  filter: string;
  pokemon: Array<string>;
  setFilter: (filter: string) => void;
  setPokemon: (pokemon: Array<string>) => void;
};

This type says that the pokemon property of your store is an array of String (which should be the lowercase string instead).

pokemon.filter(({ name: { english } }) =>

This function treats each element of the pokemon array as an object with a name property. The name itself is as an object with a property english which is a string.

These are two different and incompatible types, which is why you have an error.


You need to figure out which one is correct and which one is the mistake. The linked .txt file helps with this. A Pokemon object looks like this:

{
  id: 1,
  name: {
    english: "Bulbasaur",
    japanese: "フシギダネ",
    chinese: "妙蛙种子",
    french: "Bulbizarre"
  },
  type: ["Grass", "Poison"],
  base: {
    HP: 45,
    Attack: 49,
    Defense: 49,
    "Sp. Attack": 65,
    "Sp. Defense": 65,
    Speed: 45
  }
};

So we can see that the name property is an object with a property english -- not an Array<string>.

It is up to you how specific you want to be about your types. You could describe the name property as

type Name = Record<string, string>

which is an object whose keys and values are both string. This means that any string can be used as a key. So you might want to be more specific and say that it's an object that looks like this:

type Name = {
  english: string;
  japanese: string;
  chinese: string;
  french: string;
}

This would give you an error if you try to access an invalid key, like name.spanish.


The pokemon property of your State is an Array but it's not an array of string -- it's an array of pokemon objects. We need to define the type for a Pokemon based on your data file.

type Pokemon = {
  id: number;
  name: {
    english: string;
    japanese: string;
    chinese: string;
    french: string;
  };
  type: Array<string>;
  base: {
    HP: number;
    Attack: number;
    Defense: number;
    "Sp. Attack": number;
    "Sp. Defense": number;
    Speed: number;
  }
}

Your State type can use this Pokemon type:

type State = {
  filter: string;
  pokemon: Array<Pokemon>;
  setFilter: (filter: string) => void;
  setPokemon: (pokemon: Array<Pokemon>) => void;
};

Since you are using the generic <State> on your create function, you can fix the errors that pop up by removing the types on the arguments. They will be inferred correctly from your State type.

const useStore = create<State>((set) => ({
  // set initial values here
  filter: "",
  pokemon: [],

  setFilter: (filter) =>
    set((state) => ({
      ...state,
      filter,
    })),

  setPokemon: (pokemon) =>
    set((state) => ({
      ...state,
      pokemon,
    })),
}));

In your first version the PokemonTable did not take any props. This one is closer to correct. So you can delete your PkTableProps interface. All of the data that you need comes from the store rather than from props.

Now that the State has the correct types, your initial PokemonTable component works!

const PokemonTable = () => {
  const pokemon = useStore((state) => state.pokemon);
  const filter = useStore((state) => state.filter);

  return (
    <table width="100%">
      <tbody>
        {pokemon
          .filter(({ name: { english } }) =>
            english.toLowerCase().includes(filter.toLowerCase())
          )
          .map(({ id, name: { english }, type }) => (
            <tr key={id}>
              <td>{english}</td>
              <td>{type.join(", ")}</td>
            </tr>
          ))}
      </tbody>
    </table>
  );
};

The nested destructuring of .filter(({ name: { english } }) might be hard to read. It may be easier to understand if you write it like this.

const PokemonTable = () => {
  const pokemonArray = useStore((state) => state.pokemon);
  const filter = useStore((state) => state.filter);

  return (
    <table width="100%">
      <tbody>
      {pokemonArray
          .filter(pokemon =>
            pokemon.name.english.toLowerCase().includes(filter.toLowerCase())
          )
          .map((pokemon) => (
            <tr key={pokemon.id}>
              <td>{pokemon.name.english}</td>
              <td>{pokemon.type.join(", ")}</td>
            </tr>
          ))}
      </tbody>
    </table>
  );
};

One last thing -- you only need your useEffect which fetches the pokemon data to run once. You can give it an empty dependency array [] or a dependency array with [setPokemon].

Working CodeSandbox Link

Linda Paiste
  • 38,446
  • 6
  • 64
  • 102