You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js组件按下拉选择列排序时遇TypeScript类型错误求助

解决Next.js组件中TypeScript排序类型错误

问题根源

  • selectedColumn被声明为any类型,TypeScript无法推断它是数据对象的合法键,导致索引类型错误。
  • 列选择下拉菜单的value为大写格式(如"Id"),但数据对象的键是小写(如"id"),会导致无法正确获取属性值。
  • 未区分字符串类型(name)和数字类型(id/height)的排序逻辑,直接用减号会引发字符串排序错误。

解决方案

1. 定义明确的数据类型

先为数据项创建类型,让TypeScript明确可用的键:

type DataItem = {
  id: number;
  name: string;
  height: number;
};

2. 修正selectedColumn类型与下拉选项

将selectedColumn的类型约束为keyof DataItem | null,同时统一下拉选项的value与数据键的大小写:

const [selectedColumn, setSelectedColumn] = useState<keyof DataItem | null>(null);

// 列选择Dropdown的options修正为:
options={[
  { label: "id", value: "id" },
  { label: "name", value: "name" },
  { label: "height", value: "height" },
]}

3. 编写类型安全的排序逻辑

根据选中列的类型,分别处理数字和字符串的排序逻辑:

const orderChangeHandler = (e: { value: Order }) => {
  if (!selectedColumn) return;

  setSelectedOrder(e.value);
  const sortedData = [...data];

  sortedData.sort((a, b) => {
    const valA = a[selectedColumn];
    const valB = b[selectedColumn];

    // 数字类型排序
    if (typeof valA === "number" && typeof valB === "number") {
      return e.value === "ascending" ? valA - valB : valB - valA;
    }
    // 字符串类型排序
    if (typeof valA === "string" && typeof valB === "string") {
      return e.value === "ascending" 
        ? valA.localeCompare(valB) 
        : valB.localeCompare(valA);
    }
    return 0;
  });

  setData(sortedData);
};

完整修正后的组件代码

import React, { useState } from "react";
import classes from "./SortingBar.module.css";
import { Dropdown } from "primereact/dropdown";

type Props = {};
type Order = "ascending" | "descending";
type DataItem = {
  id: number;
  name: string;
  height: number;
};

const SortingBar = (props: Props) => {
  const [selectedOrder, setSelectedOrder] = useState<Order | null>(null);
  const [selectedColumn, setSelectedColumn] = useState<keyof DataItem | null>(null);

  const [data, setData] = useState<DataItem[]>([
    { id: 1, name: "A", height: 1.67 },
    { id: 3, name: "C", height: 1.65 },
    { id: 2, name: "B", height: 1.68 },
    { id: 4, name: "D", height: 1.7 },
    { id: 6, name: "F", height: 1.69 },
    { id: 5, name: "E", height: 1.66 },
  ]);

  const orderChangeHandler = (e: { value: Order }) => {
    if (!selectedColumn) return;

    setSelectedOrder(e.value);
    const sortedData = [...data];

    sortedData.sort((a, b) => {
      const valA = a[selectedColumn];
      const valB = b[selectedColumn];

      if (typeof valA === "number" && typeof valB === "number") {
        return e.value === "ascending" ? valA - valB : valB - valA;
      }
      if (typeof valA === "string" && typeof valB === "string") {
        return e.value === "ascending" 
          ? valA.localeCompare(valB) 
          : valB.localeCompare(valA);
      }
      return 0;
    });

    setData(sortedData);
  };

  return (
    <div>
      <div className={classes.sidebarContent}>
        <div className={classes.description}>
          <h5 className=" text-gray-800 mt-3 mb-3">Sorting</h5>
        </div>
        <div>
          <div className="col-12">
            <h6>Select field </h6>
            <Dropdown
              className="w-full"
              placeholder="Select column"
              options={[
                { label: "id", value: "id" },
                { label: "name", value: "name" },
                { label: "height", value: "height" },
              ]}
              onChange={(e) => setSelectedColumn(e.value)}
              value={selectedColumn}
            />
          </div>
          <div className="col-12">
            <h6>Select order </h6>
            <Dropdown
              className="w-full"
              placeholder="Select order"
              options={[
                { label: "Ascending", value: "ascending" },
                { label: "Descending", value: "descending" },
              ]}
              onChange={orderChangeHandler}
              value={selectedOrder}
            />
          </div>
          <div className="col-12">
            {data.map((item) => (
              <div key={item.id} className="flex flex-wrap align-items-center">
                <div className="col-4">{item.id}</div>
                <div className="col-4">{item.name}</div>
                <div className="col-4">{item.height}</div>
              </div>
            ))}
          </div>
        </div>
      </div>
    </div>
  );
};

export default SortingBar;

关键说明

  • 使用keyof DataItem约束selectedColumn,确保它只能是数据对象的合法键,彻底消除TypeScript索引错误。
  • 统一下拉选项的value与数据键的大小写,避免属性获取失败。
  • 区分数字和字符串的排序逻辑,保证不同类型数据都能正确排序,同时避免类型错误。

内容的提问来源于stack exchange,提问作者Mustafa KANLI

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 22:25:07