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

TypeScript映射类型结合条件类型未生效,无法校验非法字段

问题分析

问题出在board的类型标注上:你直接把board标注为BoardType,而BoardType中的columns被定义为Array<ColumnType>,这会让TypeScript丢失数组中每个列的具体id字面量类型,只保留string类型。因此PulseType<typeof board.columns>中的Columns[number]["id"]会被解析为string,导致data对象的键被允许是任意string,自然不会对非法键"ehh"给出警告。

解决方案

有两种可行的修复方式:

方式一:移除board的显式类型标注,保留完整字面量类型

去掉board的: BoardType标注,同时用as const强制TypeScript保留数组中每个列的具体字面量类型,让PulseType能正确提取合法的列id:

type ElementType = "ShortText" | "Status" | "Number";

type Column = {
  id: string;
  name: string;
  element: ElementType;
};

type StatusType = Column & {
  options: { option: string; color: string }[];
};

type ColumnType = Column | StatusType;

type ColumnValue<T extends ElementType> = T extends "ShortText"
  ? string
  : T extends "Status"
  ? string
  : T extends "Number"
  ? number
  : never;

type PulseType<Columns extends ColumnType[]> = {
  id: string;
  board: string;
  data: {
    [K in Columns[number]["id"]]?: ColumnValue<Extract<Columns[number], { id: K }>["element"]>;
  };
};

// 移除显式类型标注,用as const锁定字面量类型
const board = {
  id: "board-1",
  name: "Sample Board",
  columns: [
    { id: "Name", name: "Full Name", element: "ShortText" },
    {
      id: "Status",
      name: "To DOs",
      element: "Status",
      options: [
        { option: "To Do", color: "grey" },
        { option: "In Progress", color: "blue" },
        { option: "Done", color: "green" },
      ],
    },
    { id: "Age", name: "Age", element: "Number" },
  ],
} as const;

const pulses: PulseType<typeof board.columns> = {
  id: "1",
  board: "board-1",
  data: {
    Name: "John Doe",
    Status: "Done",
    Age: 25,
    ehh: "error", // 现在会正确报错:类型"{ Name: string; Status: string; Age: number; ehh: string; }"不能赋值给类型"Partial<Record<"Name" | "Status" | "Age", string | number>>"
  },
};

方式二:将BoardType改为泛型,显式保留列的具体类型

如果需要明确标注board的类型,可以把BoardType改成泛型,让它接收列数组的具体类型作为参数:

type ElementType = "ShortText" | "Status" | "Number";

type Column = {
  id: string;
  name: string;
  element: ElementType;
};

type StatusType = Column & {
  options: { option: string; color: string }[];
};

type ColumnType = Column | StatusType;

// 泛型BoardType,保留columns的具体类型信息
type BoardType<Columns extends ColumnType[]> = {
  id: string;
  name: string;
  columns: Columns;
};

type ColumnValue<T extends ElementType> = T extends "ShortText"
  ? string
  : T extends "Status"
  ? string
  : T extends "Number"
  ? number
  : never;

type PulseType<Columns extends ColumnType[]> = {
  id: string;
  board: string;
  data: {
    [K in Columns[number]["id"]]?: ColumnValue<Extract<Columns[number], { id: K }>["element"]>;
  };
};

// 用泛型标注board,传入具体的列类型数组
const board: BoardType<[
  { id: "Name"; name: string; element: "ShortText" },
  StatusType & { id: "Status"; element: "Status" },
  { id: "Age"; name: string; element: "Number" }
]> = {
  id: "board-1",
  name: "Sample Board",
  columns: [
    { id: "Name", name: "Full Name", element: "ShortText" },
    {
      id: "Status",
      name: "To DOs",
      element: "Status",
      options: [
        { option: "To Do", color: "grey" },
        { option: "In Progress", color: "blue" },
        { option: "Done", color: "green" },
      ],
    },
    { id: "Age", name: "Age", element: "Number" },
  ],
};

const pulses: PulseType<typeof board.columns> = {
  id: "1",
  board: "board-1",
  data: {
    Name: "John Doe",
    Status: "Done",
    Age: 25,
    ehh: "error", // 同样会触发类型错误
  },
};
关键说明
  • 核心问题是类型拓宽:当你用BoardType标注board时,数组中每个列的id从字面量类型(如"Name")被拓宽为string,导致PulseType无法识别具体的合法键。
  • 使用as const可以强制TypeScript保留字面量类型,避免拓宽;泛型BoardType则通过显式传递类型参数,保留列的具体结构信息。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 18:56:16