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

如何用TypeScript泛型简洁实现互斥属性组合约束?

问题:用泛型简化互斥属性组合的TypeScript类型定义

我需要定义一个TypeScript类型,要求用户只能传入以下两种属性组合之一:

  • data + color + hasColor
  • data + info + hasInfo
    任何其他组合都不允许。

原可行但繁琐的实现

这种写法能满足需求,但代码过于啰嗦:

type Props = {
  data: string;
} & (
  | {
      info?: string;
      hasInfo?: boolean;
      color?: never;
      hasColor?: never;
    }
  | {
      info?: never;
      hasInfo?: never;
      color?: string;
      hasColor?: boolean;
    }
);

function foo(props: Props) {
  console.log("bar");
}

foo({ data: "hello", info: "hello", hasInfo: true }); // TypeScript 无报错
foo({ data: "hello", info: "hello", hasColor: true }); // TypeScript 报错

我想知道有没有用泛型实现的更简洁方式?

我尝试的泛型写法(存在问题)

下面的写法逻辑有问题,导致合法调用也报错:

type Info = { info: string; hasInfo: boolean };
type Color = { color: string; hasColor: boolean };
type Data = { data: string };

function foo<T>(
  props: keyof T extends keyof Info ? Data & Info : Data & Color
) {
  console.log("bar");
}

foo({ data: "hello", color: "hello", hasColor: true }); // TypeScript 报错

泛型简化方案

可以通过泛型约束+交叉类型实现更简洁的定义,核心是让泛型仅约束在两组互斥属性范围内,再和公共属性Data交叉:

允许仅传data的版本(属性可选)

type Info = { info?: string; hasInfo?: boolean };
type Color = { color?: string; hasColor?: boolean };
type Data = { data: string };

// 约束泛型T只能是Info或Color的子类型
function foo<T extends Info | Color>(props: Data & T) {
  console.log("bar");
}

// 合法调用
foo({ data: "hello", info: "hello", hasInfo: true });
foo({ data: "hello", color: "hello", hasColor: true });
foo({ data: "hello" }); // 仅传data也允许

// 非法调用(同时包含两组属性)
foo({ data: "hello", info: "hello", hasColor: true }); // TypeScript 报错

必须传入对应组属性的版本(属性必填)

如果要求不能只传data,必须搭配某一组的属性,可将Info和Color的属性设为必填:

type Info = { info: string; hasInfo: boolean };
type Color = { color: string; hasColor: boolean };
type Data = { data: string };

function foo<T extends Info | Color>(props: Data & T) {
  console.log("bar");
}

// 合法调用
foo({ data: "hello", info: "hello", hasInfo: true });
foo({ data: "hello", color: "hello", hasColor: true });

// 非法调用
foo({ data: "hello" }); // 报错:缺少Info或Color的属性
foo({ data: "hello", info: "hello", hasColor: true }); // 报错:同时包含两组属性

原泛型写法的问题原因

你之前的写法中,keyof T extends keyof Info的逻辑存在缺陷:当传入包含color的对象时,泛型T会被推断为Data & Color,此时keyof T包含"data",而"data"不属于keyof Info,导致条件判断错误,最终返回的类型和传入的属性不匹配,引发报错。

而新方案直接约束T为Info或Color的子类型,再和Data交叉,避免了data属性干扰类型判断的问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 19:50:44