Ste*_*Koo 7 typescript reactjs typescript2.0
我经常需要定义一个类型对象,其中只有当该类型的另一个属性是某个值时才接受属性键。
\n\n一个简单的例子(在 React 的上下文中,但应该适用于任何情况)是我需要一个Button接受以下属性的类型对象:
type Button = {\n size: 'small' | 'large';\n appearance: 'solid' | 'outline' | 'minimal';\n isDisabled?: boolean;\n hasFancyOutline?: boolean;\n}\nRun Code Online (Sandbox Code Playgroud)\n\n现在,我实际上不希望类型接受hasFancyOutlineif appearanceis notoutline和isDisabledis false。
正确的方法是:
\n\ntype SharedButtonProps = {\n size: 'small' | 'large';\n}\n\ntype NonOutlineButtonProps = SharedButtonProps & {\n appearance: solid' | 'minimal';\n isDisabled?: boolean;\n}\n\ntype OutlineButtonProps = SharedButtonProps & {\n appearance: 'outline';\n isDisabled: false;\n hasFancyOutline?: boolean;\n}\n\ntype Button = NonOutlineButtonProps | OutlineButtonProps\nRun Code Online (Sandbox Code Playgroud)\n\n我想编写一个名为的速记实用程序类型ConditionalProps,它可以智能地为我完成此操作。像这样的东西:
type Button = ConditionalProps<\n {\n size: 'small' | 'large';\n appearance: 'solid' | 'outline' | 'minimal';\n isDisabled?: boolean;\n },\n {\n appearance: 'outline';\n isDisabled: false;\n hasFancyOutline?: boolean;\n }\n>\nRun Code Online (Sandbox Code Playgroud)\n\n我在想伪代码,它的工作原理如下:
\n\ntype ConditionalProps<BaseProps, ConditionalProps> = {\n // 1. Find keys with the same name in BaseProps & ConditionalProps. Optional and non-optional types such as `isDisabled?` and `isDisabled` need to be matched.\n\n type MatchingProps = Match<BaseProps, ConditionalProps> // { appearance: 'solid' | 'outline' | 'minimal', isDisabled?: boolean }\n\n type SharedProps = Omit<BaseProps, MatchingProps> // { size: 'small' | 'large' }\n\n // 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'\n\n type FailConditionProps = RemainingValues<MatchingProps, ConditionalProps> // { appearance: 'solid' | 'minimal'; isDisabled?: boolean; }\n\n // 3. Assemble\n\n type FailConditionPlusSharedProps = SharedProps & FailConditionProps\n\n type PassConditionPlusSharedProps = SharedProps & ConditionalProps\n\n return FailConditionPlusSharedProps | PassConditionPlusSharedProps\n}\nRun Code Online (Sandbox Code Playgroud)\n\n下面提香的回答就是这个问题的确切解决方案。但我想知道是否有办法重写得ConditionalProps更好。
我发现自己编写了很多类型,这些类型都以给定的值为条件。
\n\n例如,
\n\n type Button = {\n size: 'small' | 'large';\n isReallyBig?: boolean;\n appearance: 'solid' | 'outline' | 'minimal';\n hasFancyOutline?: boolean;\n outlineBackgroundColor: string;\n isDisabled?: boolean;\n isLoading?: boolean;\n }\nRun Code Online (Sandbox Code Playgroud)\n\n说我想做:
\n\nisReallyBig?仅当以下情况时才被接受size = 'large'hasFancyOutline?仅当&outlineBackgroundColor时才接受appearance = \xe2\x80\x98outline\xe2\x80\x99&isDisabled = falseisLoading只能是true如果isDisabled = true.如果我想重写ConditionalProps以清晰地定义这种类型,我该怎么做?我想实施会是这样的:
type Button = ConditionalProps<\n {\n size: 'small' | 'large';\n appearance: 'solid' | 'outline' | 'minimal';\n outlineBackgroundColor: string;\n isDisabled?: boolean;\n },\n [\n [\n { size: 'large' },\n { isReallyBig?: boolean }\n ], [\n { appearance: 'outline', isDisabled: false },\n { hasFancyOutline?: boolean }\n ], [\n { isDisabled: true },\n { isLoading?: boolean }\n ]\n ]\n >\nRun Code Online (Sandbox Code Playgroud)\n\n这样的事情可以实现吗,或者有更好的方法来处理这种情况吗?
\n在实现这一点时,我遇到的问题是,为什么只appearance应该从常见情况中删除它的值并不明显。isDisabled是 so 的并集true | false,从常见情况中删除所有值将导致从默认情况中false删除。isDisabled这可能不是所需的行为。
如果我们添加一个属性来说明判别式是什么,我们就可以构建您想要的类型
type Button = ConditionalProps<
{
size: 'small' | 'large';
appearance: 'solid' | 'outline' | 'minimal';
isDisabled?: boolean;
}, 'appearance',
{
appearance: 'outline';
isDisabled: false;
hasFancyOutline?: boolean;
}
>
type RemoveCommonValues<T, TOmit> = {
[P in keyof T]: TOmit extends Record<P, infer U> ? Exclude<T[P], U> : T[P]
}
type Omit<T, K extends PropertyKey> = Pick<T, Exclude<keyof T, K>> // not needed in 3.5
type Id<T> = {} & { [P in keyof T]: T[P] } // flatens out the types to make them more readable can be removed
type ConditionalProps<T, TKey extends keyof TCase, TCase extends Partial<T>> =
Id<Omit<T, keyof TCase> & TCase>
| Id<RemoveCommonValues<T, Pick<TCase, TKey>>>
Run Code Online (Sandbox Code Playgroud)
RemoveCommonValues遍历公共属性,如果定义了它们,则从TOmit公共值中删除在那里定义的值。为了获取案例定义的属性TOmit,我们需要获取公共属性 ( Omit<T, keyof TOmit>) 并将它们与 相交TOmit。
测试一下:
type Button = ConditionalProps<
{
size: 'small' | 'large';
appearance: 'solid' | 'outline' | 'minimal';
isDisabled?: boolean;
}, 'appearance',
{
appearance: 'outline';
isDisabled: false;
hasFancyOutline?: boolean;
}
>
// same as
type Button = {
size: "small" | "large";
appearance: "outline";
isDisabled: false;
hasFancyOutline?: boolean | undefined;
} | {
size: "small" | "large";
appearance: "solid" | "minimal";
isDisabled?: boolean | undefined;
}
Run Code Online (Sandbox Code Playgroud)
我们可以在多种情况下传递:
type Button = ConditionalProps<
{
size: 'small' | 'large';
appearance: 'solid' | 'outline' | 'minimal';
isDisabled?: boolean;
}, 'appearance' ,{
appearance: 'outline';
isDisabled: false;
hasFancyOutline?: boolean;
} | {
appearance: 'minimal';
isDisabled: false;
useReadableFont?: boolean;
}
>
// same as
type Button = {
size: "small" | "large";
appearance: "outline";
isDisabled: false;
hasFancyOutline?: boolean | undefined;
} | {
size: "small" | "large";
appearance: "minimal";
isDisabled: false;
useReadableFont?: boolean | undefined;
} | {
size: "small" | "large";
appearance: "solid";
isDisabled?: boolean | undefined;
}
Run Code Online (Sandbox Code Playgroud)
如果我们想要有更多的判别键,目前还不清楚这将如何工作,因为这并不能很好地组合。您可以传入多个键,但必须确保传入的情况涵盖所有可能的组合,因为任何值都将从结果中删除:
type Button = ConditionalProps<
{
size: 'small' | 'large';
appearance: 'solid' | 'outline' | 'minimal';
isDisabled?: boolean;
}, 'appearance' | 'size' ,{
appearance: 'outline';
size: 'small'
isDisabled: false;
hasFancyOutline?: boolean;
} | {
appearance: 'minimal';
size: 'small'
isDisabled: false;
hasFancyOutline?: boolean;
}
>
// same as
type Button = {
appearance: "outline";
size: "small";
isDisabled: false;
hasFancyOutline?: boolean | undefined;
} | {
appearance: "minimal";
size: "small";
isDisabled: false;
hasFancyOutline?: boolean | undefined;
} | {
size: "large";
appearance: "solid";
isDisabled?: boolean | undefined;
}
Run Code Online (Sandbox Code Playgroud)
没有minimal large按钮是不可能的。
| 归档时间: |
|
| 查看次数: |
2856 次 |
| 最近记录: |