开发者问题收集

Reactjs Typescript:类型“never”上不存在属性“toLowerCase”

2019-08-06
2242

我正在尝试使用 tsx 在 reactjs 中过滤具有多个键值的数据。 Cards.tsx 是父组件, ShipmentCard.tsx 是子组件。我收到 Property 'toLowerCase' does not exist on type 'never' 错误。我只想根据搜索条件返回相关对象。有人可以告诉我我在哪里犯了错误吗?

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;
{
  "shipments": [
    {
      "id": "123",
      "name": "Heromisha",
      "total": "1000",
      "status": "ACTIVE",
      "userId": "E222"
    },
    {
      "id": "456",
      "name": "Honda",
      "total": "3000",
      "status": "ACTIVE",
      "userId": "E111"
    }
  ]
}
2个回答

编译器无法识别 state.card[...] 的真实类型,因为它被声明为数组。 最简单的方法是将其声明为 any[]

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

或者明确描述数据类型

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

或者通过对象示例

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

type Card = typeof cardSample;

    ...
          state = {
            card: [] as Card[],
            filter: ""
          };
Nail Achmedzhanov
2019-08-06

Component 是一个通用接口,具有两个类型参数:props 的类型和 state 的类型。您的组件似乎没有 props,因此您可以只使用 object{ 作为 props 接口,但您有 state,因此您需要说明该 state 具有什么 shape

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: ""
  };
  // ...
}

另外,我怀疑您会遇到的下几个问题是:

  1. 您没有检查 fetch 是否成功。您并不是唯一遇到这种情况的人,这是一个 非常 常见的错误(如此常见,我 在我的贫血小博客上写下了 它)。您需要在 fetch 调用中添加检查:

    fetch("http://localhost:3001/shipments")
    .then(response => {
    if (!response.ok) {
    throw new Error("HTTP error " + response.status);
    }
    return response.json();
    })
    // ...
    
  2. 您没有处理来自 fetch 的错误。 loadShipmentList 应该返回承诺链(然后 componentDidMount 和使用它的其他地方会处理错误)或自己处理/报告错误。

  3. 您正在将 card 设置为解析 JSON 的结果。您的代码假设 card 是一个数组,但 JSON 的顶层不是数组,它是一个具有 shipments 属性的对象(它是一个数组)。我怀疑您的意思是将 card (应该是 cards ,复数,或 shipments )设置为返回数据的 shipments 属性:

    .then(({shipments}) => this.setState({ car​​d: shippings }));
    // ^^ ^^ ^^^^^^^^^
    

可能还有其他问题,但上述主要答案应该解决了 never 问题,希望这些进一步的提示有所帮助。


在一条评论中,您说当您为 card 指定类型时,此代码会导致错误:

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

如果您希望 JSON 中的数据形状可变,则可以将 card 声明为 any[] (或者可能是 Record<string,string>[] ),然后该动态代码就可以正常工作。但前提是您希望组件由 JSON 驱动。否则,请更新代码以使用类型安全的属性名称:

const filteredData = this.state.card.filter(({id, name, total, status, userId}) =>
  `${id}\t${name}\t${total}\t${status}\t${userId}`.toLowerCase().includes(lowercasedFilter)
);
T.J. Crowder
2019-08-06