React和TypeScript – 如果props存在,则条件参数类型

huangapple go评论68阅读模式
英文:

React & TypeScript - Conditional argument type if props is present

问题

在React中,我正在尝试根据组件中是否传递了一个属性来设置函数参数的条件类型。

以下是示例,我有这个组件:

const DatePicker = ({
  handleDateChange,
  value,
  objectKey,
}: {
  handleDateChange: (value: Dayjs | { [x: string]: Dayjs }) => void;
  value: Dayjs;
  objectKey?: string;
}) => JSX.Element

我想要做的是,如果"objectKey"作为属性传递了,那么函数handleDateChange中"value"参数的类型将是{ [x: string]: Dayjs },否则它将是value: Dayjs

有人知道如何实现这个吗?

英文:

In react I'm trying to set a conditional type in a function argument depending if a prop is pass in the component.

Here is the example, I have this component:

const DatePicker = ({
  handleDateChange,
  value,
  objectKey,
}: {
  handleDateChange: (value: Dayjs | { [x: string]: Dayjs }) => void;
  value: Dayjs;
  objectKey?: string;
}) => JSX

What I want to do is that, if "objectKey" is pass as props than the type of the paremeter "value" in the function handleDateChange would be { [x: string]: Dayjs }, otherwise it would be value: Dayjs.

Does anyone have an idea of how to make this?

答案1

得分: 2

你可以使用函数重载来实现类似这样的功能。

// 第一个重载签名
function DatePicker({
  handleDateChange,
  value,
}: {
  handleDateChange: (value: Dayjs) => void;
  value: Dayjs;
}): JSX;
// 第二个重载签名
function DatePicker({
  handleDateChange,
  value,
  objectKey,
}: {
  handleDateChange: (value: { [x: string]: Dayjs }) => void;
  value: Dayjs;
  objectKey?: string;
}): JSX;
// 实现部分
function DatePicker({
  handleDateChange,
  value,
  objectKey,
}: {
  handleDateChange: ((value: Dayjs) => void) | ((value: { [x: string]: Dayjs }) => void);
  value: Dayjs;
  objectKey?: string;
}) {
  // ...
}

使用这种方法,DatePicker 只能根据你的定义来进行调用,但实现部分不会知道 handleDateChangeobjectKey 之间的关联,因此仍然需要显式处理。

不知道 DatePicker 的任何详细信息,我认为最好只使用一个签名,让父组件进行更改以适应 DatePicker,而不是让 DatePicker 处理来自其父组件的特殊情况。

英文:

You can achieve something like this with function overloads.

// first overload signature
function DatePicker({
  handleDateChange,
  value,
}: {
  handleDateChange: (value: Dayjs) => void;
  value: Dayjs;
}): JSX;
// second overload signature
function DatePicker({
  handleDateChange,
  value,
  objectKey,
}: {
  handleDateChange: (value: { [x: string]: Dayjs }) => void;
  value: Dayjs;
  objectKey?: string;
}): JSX;
// implementation
function DatePicker({
  handleDateChange,
  value,
  objectKey,
}: {
  handleDateChange: ((value: Dayjs) => void) | ((value: { [x: string]: Dayjs }) => void);
  value: Dayjs;
  objectKey?: string;
}) {
  // ...
}

With this approach DatePicker can only be called according to your definition, however the implementation won't know the correlation between handleDateChange and objectKey so you still need to handle that explicitly.

Without knowing any details of DatePicker I think it would be better to only use one signature and let the parent component make changes to fit DatePicker instead of having DatePicker handle special cases from it's parents.

答案2

得分: 1

以下类型应该能够满足您的需求,使用联合类型和交叉类型

type DatePickerProps = {
    value: Dayjs;  
} & ({
    objectKey?: undefined;
    handleDateChange: (value: Dayjs) => void;
} | {
    objectKey: string;
    handleDateChange: (value: { [x: string]: Dayjs }) => void;
});

解释:

  • 由于value始终是类型的一部分,我们首先添加它以将其排除在外,并且不重复定义它。
  • 然后,我们将这部分与根据您的规则可能变化的两种情况的联合类型(|)相交(&),具体而言:
  • handleDateChange的签名根据objectKey的类型是string还是undefined(未设置)而变化。
  • 我们以不同方式定义objectKey(首先带?,然后没有)是因为我们希望让TypeScript知道在未设置时它是完全可省略的。

您可以简单地将其分配给props参数对象:

const DatePicker = ({ handleDateChange, value, objectKey }: DatePickerProps) => {
    // ...
}
英文:

The following type should do what you are interested in, using Unions and Intersections:

type DatePickerProps = {
    value: Dayjs;  
} & ({
    objectKey?: undefined;
    handleDateChange: (value: Dayjs) => void;
} | {
    objectKey: string;
    handleDateChange: (value: { [x: string]: Dayjs }) => void;
});

Explanation:

  • Since value is always part of the type, we add it first to get it out of the way, and so that we don't duplicate it.
  • Then we intersect (&) that part with the union (|) of the two situations that can vary according to your rules, specifically:
  • The signature of handleDateChange varies based on whether the type of objectKey is string or undefined (not set).
  • We define objectKey differently (first with ?, then without) because we want to let TypeScript know it is completely omittable when not set.

You can simply assign it to the props parameter object:

const DatePicker =
    ({handleDateChange, value, objectKey}: DatePickerProps) =>
        ...

huangapple
  • 本文由 发表于 2023年7月18日 04:12:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/76707790.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定