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

如何实现TypeScript中对象的多个字段同时必填或同时缺失

如何实现TypeScript中对象的多个字段同时必填或同时缺失

嗨,我来帮你搞定这个问题~你遇到的其实是TypeScript联合类型的一个常见“小漏洞”:原来的写法里,UntitledProps | TitledProps会让TypeScript默认允许你传额外的属性(比如只传title),因为它觉得这个对象可以匹配UntitledProps分支(TypeScript会忽略对象字面量的额外属性,除非你明确禁止),所以没法触发你想要的错误提示。

这里给你一个最直观的解决方案,通过never类型来强制字段的“绑定关系”:

import React from "react";

// 先定义基础的公共属性
interface BaseProps {
  id: string;
}

// 情况1:完全没有title和icon(用never禁止这两个字段有有效值)
type UntitledProps = BaseProps & {
  title?: never;
  icon?: never;
};

// 情况2:同时拥有title和icon(两个字段都必填)
type TitledProps = BaseProps & {
  title: string;
  icon: string;
};

// 最终的Props类型是两种情况的联合
type Props = UntitledProps | TitledProps;

function Component(props: Props) {
  return <h1>OK</h1>;
}

// ✅ 正常情况:符合预期,不会报错
const a = <Component id="id" />;
const b = <Component id="id" title="title" icon="icon" />;

// ❌ 现在这两个会触发你想要的错误,完美!
const c = <Component id="id" title="title" />;
const d = <Component id="id" icon="icon" />;

原理解释

当我们把title?: never和icon?: never加到UntitledProps里时,TypeScript会明确知道:在这个分支下,title和icon不仅是可选的,而且只能是undefined(或者完全不写)——因为never类型没有任何有效值。

这样一来,当你只传title或者只传icon时,这个对象既不属于UntitledProps(因为有了非undefined的title/icon,违反了never的要求),也不属于TitledProps(因为缺了另一个必填字段),自然就会抛出你预期的错误。

额外扩展:用判别式字段实现(可选)

如果你能接受多传一个标记字段,也可以用判别式的写法,比如加个hasTitle来区分两种状态:

import React from "react";

interface BaseProps {
  id: string;
}

type Props = 
  // 无标题图标分支
  (BaseProps & { hasTitle: false }) 
  // 有标题图标分支(同时必填)
  | (BaseProps & { hasTitle: true; title: string; icon: string });

function Component(props: Props) {
  return <h1>OK</h1>;
}

// ✅ 正常使用
const a = <Component id="id" hasTitle={false} />;
const b = <Component id="id" hasTitle={true} title="title" icon="icon" />;

// ❌ 缺字段会报错
const c = <Component id="id" hasTitle={true} title="title" />;

不过这种写法需要额外传hasTitle,如果你想保持API的简洁,第一种方法显然更合适~

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.08 09:14:31