我正在尝试创建一种分布式联合类型,其中一个键的传递使其他键成为必需。
interface BaseArgs {
title: string
}
interface FuncPagerArgs {
enablePager: true
limit: number
count: number
}
type FuncArgs = (FuncPagerArgs & BaseArgs) | BaseArgs;
function func(args: FuncArgs) {
if ("enablePager" in args) {
pager({ limit: args.limit, count: args.count });
}
}
interface PagerArgs {
limit: number
count: number
}
function pager(args: PagerArgs) {
}
func({
title: "something",
enablePager: true
})
对我来说,该代码不应通过验证,因为我在传递enablePager时正在调用func
,但是当{{1 }} 是真的。在我的真实示例中,我试图为3个或4个不同的特征布尔值执行此模式,每个布尔值都会更改合同以要求更多字段。不幸的是,我什至无法使用布尔型的第一个功能,更不用说多重了。
答案 0 :(得分:0)
我们在这里看到的问题与工会元素之间没有适当的区分有关。我们在两个成员(FuncPagerArgs & BaseArgs) | BaseArgs
之间存在重叠,重叠部分等于BaseArgs
。 behavior of function argument types in TS也很重要:
比较功能参数的类型时,如果源参数可分配给目标参数,分配成功,反之亦然。
这是什么意思-如果我们的类型可以分配给想要的类型,或者想要的类型可以分配给我们的参数类型,则可以传递它。考虑一下测试:
type ArgType = {
title: "something",
enablePager: true
}
type IsAssignable = ArgType extends FuncArgs ? true : false; // true, can be passed
因此,您要传递的类型可以分配给所需的类型,然后TS允许它传递。不用问我为什么,这是设计决策,您可以将其理解为一种折衷方案,更严格的要求会阻止许多想要的行为,至少在上面我链接的TS文档中已经说明过。可以肯定的是,代码内部现在存在问题,这将使我们出现运行时错误。
为了解决此问题,我们需要创建适当的,没有重叠的已区分联合。
interface BaseArgs {
title: string
}
interface OnlyTitle extends BaseArgs {
kind: 'TITLE'
}
interface FuncPagerArgs extends BaseArgs {
kind: 'PAGER'
enablePager: true
limit: number
count: number
}
type FuncArgs = OnlyTitle | FuncPagerArgs;
function func(args: FuncArgs) {
if (args.kind === 'PAGER') {
pager({ limit: args.limit, count: args.count });
}
}
interface PagerArgs {
limit: number
count: number
}
function pager(args: PagerArgs) {
}
func({
kind: "TITLE",
enablePager: true
}) // error
func({
kind: "TITLE",
title: 'title'
}) // correct
我所做的是以kind
属性形式的discriminant,它将允许适当的成员出现任何重叠。这意味着现在您只能使用一个或另一个变体,类型永远不会重叠。
在评论中,作者表示他需要将所有选项组合在一起,但是启用某些功能后,与之相关的所有选项都应包含在内。我们可以通过分组分离选项并加入它们来实现这种结构。考虑:
interface Base {
title: string
}
interface Pager {
pager?: {
limit: number
count: number
}
}
interface Sort {
sorter?: {
columns: string[] // example property
}
}
type Options = Pager & Sort & Base;
function func(args: Options) {
if (args.pager) {
pager({ limit: args.pager.limit, count: args.pager.count });
}
if (args.sorter) {
sorter(args.sorter.columns)
}
}
func({
title: 'title',
pager: {count: 2, limit: 10} // all fields its ok
})
func({
title: 'title',
pager: {count: 2} // error limit is missing
})
现在我们可以将pager
和sorter
放在一起,当您拥有pager
时,需要根据需要放置与之相关的所有选项。