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

如何基于可选kind属性缩小嵌套对象的联合类型范围?

问题分析与解决方案

你遇到的核心问题是:TLabeledField 类型没有根据可选的 kind 属性正确收窄联合类型,导致指定 kind: "number" 时传入不属于该类型的 block 属性不会触发类型错误。

原方案中使用 PartialBy 直接将 kind 设为可选,会把联合类型每个分支的 kind 都变成可选,最终合并后的类型会保留所有分支的属性,无法实现基于 kind 的类型校验。下面是修正后的实现:

类型重构方案

首先拆分类型结构,明确公共字段与各 kind 专属属性的映射关系,再重新定义支持默认 kind: "string" 的 TLabeledField:

// 提取公共字段
type TFieldCommon = {
  placeholder?: string;
  required?: boolean;
};

// 定义各kind对应的专属属性映射
type TFieldKindMap = {
  string: { value?: string; block?: boolean };
  password: { value?: string };
  email: { value?: string };
  number: { value?: number };
  boolean: { value?: boolean; inline?: boolean; toggle?: boolean };
};

// 原始TField类型(保持原有定义)
type TField = {
  field: string;
  name: string;
} & TFieldCommon & {
  [K in keyof TFieldKindMap]: { kind: K } & TFieldKindMap[K];
}[keyof TFieldKindMap];

// 基础可选字段:name、field设为可选
type TLabeledFieldBase = TFieldCommon & Partial<Pick<TField, "name" | "field">>;

// 指定kind时的类型:绑定对应kind的专属属性
type TLabeledFieldWithKind<K extends keyof TFieldKindMap> = TLabeledFieldBase & {
  kind: K;
} & TFieldKindMap[K];

// 默认类型:不指定kind时,默认对应string类型的属性
type TLabeledFieldDefault = TLabeledFieldBase & Partial<{ kind: "string" }> & TFieldKindMap["string"];

// 合并所有可能的类型
type TLabeledField = TLabeledFieldDefault | {
  [K in Exclude<keyof TFieldKindMap, "string">]: TLabeledFieldWithKind<K>;
}[Exclude<keyof TFieldKindMap, "string">];

// 保持原有集群类型定义
type TFieldCluster = {
  [key: string]: TLabeledField;
};

验证效果

将重构后的类型应用到原函数中,即可实现预期的类型校验:

function makeFields(cluster: TFieldCluster) {
  const fieldMap = Object.entries(cluster).map(([str, path]) => {
    let label = str;
    let name, field;
    let required = false;

    if (label.endsWith("*")) {
      required = true;
      label = label.slice(0, -1);
    }
    if (label.startsWith("$")) {
      field = label.slice(1);
      name = label.replace(/([A-Z])/g, " $1").toLowerCase();
    } else {
      field = label;
      name = label;
    }

    return [
      field,
      {
        type: "string",
        name,
        field,
        required,
        ...path,
      },
    ];
  });
  return Object.fromEntries(fieldMap);
}

const fields = makeFields({
  title: {}, // ✅ 合法,默认kind为string
  subtitle: { kind: "boolean", toggle: true }, // ✅ 合法,boolean类型支持toggle
  about: { block: true }, // ✅ 合法,默认string类型支持block
  count: { kind: "number", block: true }, // ❌ 报错:number类型不存在block属性
});

核心改进点

  • 拆分公共字段与专属属性,避免联合类型合并后属性混乱
  • 通过显式的类型绑定,确保指定 kind 时只能传入对应类型的属性
  • 默认分支明确绑定 string 类型的属性,实现 kind 不指定时的默认行为

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 17:43:30