简体   繁体   中英

Reactjs Typerscript: Property 'toLowerCase' does not exist on type 'never'

Im trying to filter data with multiple key value in reactjs with tsx. Cards.tsx is a parent component and ShipmentCard.tsx is a child. I'm getting Property 'toLowerCase' does not exist on type 'never' error. I just want to return the related object based on search criteria. Can anyone let me know where I made a mistake?

Cards.tsx

 class Cards extends Component { state = { card: [], filter: "" }; componentDidMount() { this.loadShipmentList(); } handleChange = (event: any) => { this.setState({ filter: event.target.value }); }; loadShipmentList() { fetch("http://localhost:3001/shipments") .then(response => response.json()) .then(data => this.setState({ card: data })); } render() { const { card, filter } = this.state; const lowercasedFilter = filter.toLowerCase(); const filteredData = this.state.card.filter(item => { return Object.keys(item).some(key => item[key].toLowerCase().includes(lowercasedFilter) ); }); return ( <Card className="Search-Bar"> <CardContent> <Grid container spacing={3}> <TextField label="Search" onChange={this.handleChange} /> </Grid> </CardContent> </Card> <Grid container spacing={3}> {filteredData.map((card: any) => ( <Grid item xs={12}> <ShipmentCard key={card.id} value={card} /> </Grid> ))} </Grid> ); } } export default Cards; 
db.json

 { "shipments": [ { "id": "123", "name": "Heromisha", "total": "1000", "status": "ACTIVE", "userId": "E222" }, { "id": "456", "name": "Honda", "total": "3000", "status": "ACTIVE", "userId": "E111" } ] } 

Component is a generic interface with two type parameters: The type of your props, and the type of your state. Your component doesn't seem to have props so you can just use object or {} for the props interface, but you do have state, so you need to say what shape that state has:

interface Card {
  id:     string;
  name:   string;
  total:  string;
  status: string;
  userId: string;
}
interface CardsState {
  card:   Card[];
  filter: string;
}
class Cards extends Component<object,CardsState> {
  state: CardsState = {
    card: [],
    filter: ""
  };
  // ...
}

Separately, the next couple of problems I suspect you'll have are:

  1. You're not checking that fetch succeeded. You're not alone in this, it's a very common error (so common I wrote it up on my anemic little blog). You need to add a check to your fetch call:

     fetch("http://localhost:3001/shipments") .then(response => { if (!response.ok) { throw new Error("HTTP error " + response.status); } return response.json(); }) // ... 
  2. You're not handling errors from fetch . loadShipmentList should either return the promise chain (and then componentDidMount and other places it's used would handle errors) or handle/report errors itself.

  3. You're setting card to the result of parsing your JSON. Your code assumes card is an array, but the JSON's top level isn't an array, it's an object with a shipments property (which is an array). I suspect you meant to set card (which should be cards , plural, or shipments ) to the shipments property of the returned data:

     .then(({shipments}) => this.setState({ card: shipments })); // ^^ ^^ ^^^^^^^^^ 

There may be further issues, but the main answer above should address the never issue, and hopefully these further hints have helped.


In a comment you've said that when you give card a type, this code causes an error:

const filteredData = this.state.card.filter(item => {
  return Object.keys(item).some(key =>
    item[key].toLowerCase().includes(lowercasedFilter)
  );
});

If you expect the shape of the data in the JSON to be variable, you could declare card as any[] (or perhaps Record<string,string>[] ) and then that dynamic code would work. But only if you want the component driven by the JSON. Otherwise, update the code to use the typesafe property names:

const filteredData = this.state.card.filter(({id, name, total, status, userId}) =>
  `${id}\t${name}\t${total}\t${status}\t${userId}`.toLowerCase().includes(lowercasedFilter)
);

The compiler doesn't real type of state.card[...], becouse it's declared as array. The most simple way is to declare this as any[]

  state = {
    card: [] as any[],
    filter: ""
  };

Or describe data type explicit

interface Card {
    "id": string,
    "name": string,
    "total": string,
    "status": string,
    "userId": string
}
...
      state = {
        card: [] as Card[],
        filter: ""
      };

Or by object example

const cardSample = {
      "id": "123",
      "name": "Heromisha",
      "total": "1000",
      "status": "ACTIVE",
      "userId": "E222"
  };

type Card = typeof cardSample;

    ...
          state = {
            card: [] as Card[],
            filter: ""
          };

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM