如果多次传递了另一个参数,则打字稿需要参数

时间:2020-02-14 05:24:38

标签: typescript

我正在尝试创建一种分布式联合类型,其中一个键的传递使其他键成为必需。

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
})

https://www.typescriptlang.org/play/index.html?ssl=1&ssc=1&pln=31&pc=3#code/JYOwLgpgTgZghgYwgAgEJwM4QIJQOYbIDeAUMucmMGADYQBcyGYUoeJAviSaJLIigBiAVxAIACnDzRcBYmQoQQcAEZ1J0qIxbCIC8jWABbaoxDCjK6PuQIA9qLBmLVqJ25gAngAchohLKEALzIABQiYhoy+IQAZGiYODEAlMgAPglYgQDc3DD+VHYgyPlioXAxjBEBKfIUyMAwYQBESqrqUtDNDcUVBKmk9fXenVChRMiGJk7IfRgAdFPUADS2DuCMc-P2jsgcybn1XFw84NDwSMhRUIF1FEsz5pbW9TsbyE+u7iSlCIXFI005UqV1GgQG3BOv3GNiotAYyGaGDsRggYAAFmxmssbG01BBrtooLpOMkgA

对我来说,该代码不应通过验证,因为我在传递enablePager时正在调用func,但是当{{1 }} 是真的。在我的真实示例中,我试图为3个或4个不同的特征布尔值执行此模式,每个布尔值都会更改合同以要求更多字段。不幸的是,我什至无法使用布尔型的第一个功能,更不用说多重了。

1 个答案:

答案 0 :(得分:0)

我们在这里看到的问题与工会元素之间没有适当的区分有关。我们在两个成员(FuncPagerArgs & BaseArgs) | BaseArgs之间存在重叠,重叠部分等于BaseArgsbehavior 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
})

现在我们可以将pagersorter放在一起,当您拥有pager时,需要根据需要放置与之相关的所有选项。