我们有一个如下所示的结构:
export type LinkRestSource = {
model: string;
rel?: string;
title?: string;
} | {
model?: string;
rel: string;
title?: string;
} | {
model?: string;
rel?: string;
title: string;
};
这几乎等于说
type LinkRestSource = Partial<{model: string, rel: string, title: string}>
只不过这将允许传入一个空对象,而初始类型要求传入一个属性
我如何创建一个像Partial
这样的泛型,但它的行为与上面的结构类似?
7条答案
按热度按时间mklgxw1f1#
我想我有一个解决方案给你。你正在寻找一个接受
T
类型并产生一个包含T
* 至少一个 * 属性的相关类型的东西。也就是说,它类似于Partial<T>
,但排除了空对象。如果是这样的话,这里是:
要剖析它:首先,
AtLeastOne<T>
是Partial<T>
与 something 的交集。U[keyof U]
意味着它是U
的所有属性值的并集。(的默认值)U
设置为Map类型,其中T
的每个属性都Map到Pick<T, K>
,Pick<T, K>
是键K
的单属性类型。(例如,Pick<{foo: string, bar: number},'foo'>
等效于{foo: string}
...它从原始类型中“挑选”'foo'
属性。)这意味着在这种情况下,U[keyof U]
是T
中所有可能的单属性类型的并集。嗯,这可能会让人感到困惑。让我们一步一步地看看它是如何在下面的具体类型上操作的:
扩展到
或
或
或
或
或
或
或
我想这正是你想要的。
您可以测试一下:
你觉得这样行吗?祝你好运!
0aydgbwb2#
如果你知道你想要的属性,还有另一个解决方案。
这也将允许您锁定同一类型的多个密钥,例如
bihw5rsg3#
jcalz提供的解决方案的简化版本:
type AtLeastOne<T> = { [K in keyof T]: Pick<T, K> }[keyof T]
所以整个实现变成了
这是TSPlayground的链接,可以尝试一下
whhtz7ly4#
不幸的是,上面的答案对我不起作用。
这可能是因为编译器无法捕获错误,也可能是因为我的IDE无法检索对象的预期属性,即使对象的类型已被注解。
以下运行良好,取自official microsoft azure/keyvault-certificates软件包:
egdjgwm85#
也许是这样的:
但是有点乱:)
或
neekobn86#
另一种方法,如果你需要保留一些必需的属性和至少一个其余的必需太。见打字脚本Playground的例子。
基本界面可能如下所示:
......如果您只需要“email”、“cellphone”和“facebookId”中的至少一个,请更改和合并每个属性的接口,而不使用可选符号:
结果如下所示:
dced5bon7#
在我的例子中,我希望至少有一个属性是***实际设置的***(而不仅仅是从并集中提取的,其中一些路径的值是设计的
undefined
)。我能想出的最简单的公式是...
我没有发现上面的任何一种更简洁的方法在处理像
{concurrent:number} | {concurrent?:never}
这样的复杂联合体时起作用,更冗长的联合体看起来很可怕,我宁愿完全理解我的类型。我的方法收敛于gafi的解决方案的一个变体,即
type AtLeastOne<T> = { [K in keyof T]: Pick<T, K> }[keyof T]
,但关键是我从Required<T>
中选取,否则undefined
仍然在来自我的类型联合的有效属性值集中(因此它仍然不排除空对象)。应该可以单独使用上面的方法,但是下面给出了我定义一个非空
ScheduleOptions
类型的完整解决方案(由一个复杂的联合体支持),作为参考。这个例子还显示了AllOrNothing<T>
的定义,它可能是这类问题的补充类型...