【发布时间】:2019-06-04 05:21:37
【问题描述】:
我经常需要定义一个类型对象,其中只有当该类型的另一个/多个属性是某个值时才接受属性键。
一个简单的例子(在 React 的上下文中,但应该适用于任何情况)是我需要一个类型为 Button 的对象,它接受以下属性:
type Button = {
size: 'small' | 'large';
appearance: 'solid' | 'outline' | 'minimal';
isDisabled?: boolean;
hasFancyOutline?: boolean;
}
现在,如果appearance 不是outline 并且isDisabled 是false,我实际上不希望该类型接受hasFancyOutline。
正确的做法是:
type SharedButtonProps = {
size: 'small' | 'large';
}
type NonOutlineButtonProps = SharedButtonProps & {
appearance: solid' | 'minimal';
isDisabled?: boolean;
}
type OutlineButtonProps = SharedButtonProps & {
appearance: 'outline';
isDisabled: false;
hasFancyOutline?: boolean;
}
type Button = NonOutlineButtonProps | OutlineButtonProps
我想编写一个名为ConditionalProps 的速记实用程序类型,它可以智能地为我执行此操作。像这样的:
type Button = ConditionalProps<
{
size: 'small' | 'large';
appearance: 'solid' | 'outline' | 'minimal';
isDisabled?: boolean;
},
{
appearance: 'outline';
isDisabled: false;
hasFancyOutline?: boolean;
}
>
我在用伪代码思考,它会像这样工作:
type ConditionalProps<BaseProps, ConditionalProps> = {
// 1. Find keys with the same name in BaseProps & ConditionalProps. Optional and non-optional types such as `isDisabled?` and `isDisabled` need to be matched.
type MatchingProps = Match<BaseProps, ConditionalProps> // { appearance: 'solid' | 'outline' | 'minimal', isDisabled?: boolean }
type SharedProps = Omit<BaseProps, MatchingProps> // { size: 'small' | 'large' }
// 2. Find what's the values of the props if they don't match the condition, e.g. 'appearance' would be either 'solid' or 'minimal'
type FailConditionProps = RemainingValues<MatchingProps, ConditionalProps> // { appearance: 'solid' | 'minimal'; isDisabled?: boolean; }
// 3. Assemble
type FailConditionPlusSharedProps = SharedProps & FailConditionProps
type PassConditionPlusSharedProps = SharedProps & ConditionalProps
return FailConditionPlusSharedProps | PassConditionPlusSharedProps
}
编辑
以下提香的回答是对此的确切解决方案。但我想知道是否有办法重写ConditionalProps 使其变得更好。
我发现自己编写了很多以给定值为条件的类型。
例如,
type Button = {
size: 'small' | 'large';
isReallyBig?: boolean;
appearance: 'solid' | 'outline' | 'minimal';
hasFancyOutline?: boolean;
outlineBackgroundColor: string;
isDisabled?: boolean;
isLoading?: boolean;
}
说我想做:
-
仅当
size = 'large'时才接受isReallyBig? -
仅当
appearance = ‘outline’和isDisabled = false时才接受hasFancyOutline?和outlineBackgroundColor -
仅当
isDisabled = true时,isLoading才能为true。
如果我想重写ConditionalProps 来干净地定义这种类型,我该怎么做?我在想实现会是这样的:
type Button = ConditionalProps<
{
size: 'small' | 'large';
appearance: 'solid' | 'outline' | 'minimal';
outlineBackgroundColor: string;
isDisabled?: boolean;
},
[
[
{ size: 'large' },
{ isReallyBig?: boolean }
], [
{ appearance: 'outline', isDisabled: false },
{ hasFancyOutline?: boolean }
], [
{ isDisabled: true },
{ isLoading?: boolean }
]
]
>
这样的事情是可以实现的,还是有更好的方法来处理这种情况?
【问题讨论】:
标签: reactjs typescript typescript2.0