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

如何用TypeScript实现InputType指定属性至少一个必填及访问?

TypeScript 类型约束与属性访问问题

问题背景

我定义了以下 TypeScript 类型:

export type InputType = {
  configJsonUrl?: string;
  configJsObject?: DataType;
  rawData?: { [key: string]: string }; 
  action?: () => void;
};

export type DataType = {
  id: string;
  type: string;
  'product-configuration': { input: string };
};

对于 InputType,configJsonUrl、configJsObject、rawData 三个属性本身是可选的,但使用该类型的 React 组件要求至少传入其中一个,示例用法如下:

<ComponentX configJsonUrl='./data/info.json' />
<ComponentX configJsObject={jsObject} />
<ComponentX rawData={adhocData} />

我知道可以通过统一 input 字段的方式实现必填约束,但希望保留这三个属性的原名来做条件渲染,请问该怎么实现?这在 TypeScript 中可行吗?

我的 React 组件当前写法如下:

const FormGenerator = ({
  configJsonUrl,
  configJsObject,
  rawData,
}: InputType ): JSX.Element => {
  const [propForForm, setPropForForm] = useState<string>('dummy');
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    if (configJsonUrl) {      
          setPropForForm("URL");
          setLoading(false); // Stop loading
    } else if (rawData) {
          setPropForForm("adhoc object");
          setLoading(false); // Stop loading
   
    } else if (configJsObject) {
          setPropForForm("JS object");
          setLoading(false); // Stop loading      
    }
  }, [configJsonUrl, rawData, configJsObject]);

// 其余渲染逻辑
}

后来根据建议修改了 InputType:

export type InputType = {
  input: {configJsonUrl: string} | {configJsObject: DataType} | {rawData: {[key: string]: string}}
  action?: () => void;
};

对应的组件改为:

const FormGenerator = ({input}: InputType )
// 其余渲染逻辑

现在想请教:如何从 input 中访问 configJsonUrl、configJsObject 和 rawData 这三个属性?


解决方案

一、保留原属性名同时实现「至少传一个」的类型约束

如果想保留三个属性的原名,同时强制要求至少传入其中一个,可以通过联合类型定义 InputType,替代原本的可选属性写法:

export type InputType = 
  // 仅传入 configJsonUrl 的情况
  { configJsonUrl: string; configJsObject?: never; rawData?: never; action?: () => void } |
  // 仅传入 configJsObject 的情况
  { configJsObject: DataType; configJsonUrl?: never; rawData?: never; action?: () => void } |
  // 仅传入 rawData 的情况
  { rawData: { [key: string]: string }; configJsonUrl?: never; configJsObject?: never; action?: () => void };

这里用 ?: never 约束未传入的属性不能赋值,确保每次只能传入三个中的一个(若业务允许同时传多个,可去掉对应属性的 ?: never 限制)。

这种写法下,组件依然可以直接解构三个属性,TypeScript 会自动推断哪个属性有值,原有的条件渲染逻辑无需改动,还能在编译阶段拦截「一个属性都不传」的错误。

二、统一 input 字段后的属性访问方式

如果已经采用了统一 input 字段的写法,可通过类型守卫判断 input 的具体类型,从而安全访问对应属性:

const FormGenerator = ({ input, action }: InputType ): JSX.Element => {
  const [propForForm, setPropForForm] = useState<string>('dummy');
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    setLoading(true);
    // 类型守卫:判断 input 是否包含 configJsonUrl 属性
    if ('configJsonUrl' in input) {
      setPropForForm("URL");
      // 安全访问 input.configJsonUrl
      console.log(input.configJsonUrl);
    } else if ('rawData' in input) {
      setPropForForm("adhoc object");
      console.log(input.rawData);
    } else if ('configJsObject' in input) {
      setPropForForm("JS object");
      console.log(input.configJsObject);
    }
    setLoading(false);
  }, [input]);

  // 其余渲染逻辑
};

TypeScript 会在类型守卫的分支中自动缩小 input 的类型范围,因此每个分支内都能安全访问对应属性,不会出现类型报错。若业务允许同时传入多个属性,可将 else if 改为独立的 if 判断。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 21:54:51