使TypeScript中需要一个函数参数

时间:2020-09-08 20:09:16

标签: typescript

在以下代码中:

type NoArg = {
  (): void
}

type OneArg = {
  (x:number): void
}

let noArg: NoArg = (x:number)=>{}
let oneArg: OneArg = ()=>{}

只有第一个分配的对象会生成编译器错误。我理解为什么会这样,因为JavaScript允许传递的函数少于其完整的可能参数集,而这不同于说参数是可选的,这与调用而不是通过。参见the FAQ

但是,有没有办法构造一个与零参数函数不兼容的OneArg接口版本?

我了解这可以通过品牌或名义上的输入来完成,例如

type OneArg = {
  (x:number): void
  _brand: “OneArg”
}

但是,这或任何其他类型的名义键入解决方案都需要在赋值上做额外的工作(例如,您必须将_brand属性显式添加到函数中)。

所以我的问题是-是否有任何方法可以构造简单的赋值let oneArg: OneArg = ()=>{}失败的NoArg类型?

上面链接的FAQ说:“目前TypeScript中没有一种方法可以指示必须存在回调参数。”这是否完全排除了我想在此处执行的操作?我希望它不是,因为这不是回调参数,但也许原理是相同的。

更新:在下面的评论中,有人提出了是否可以使用类型保护测试来实现的问题。据我所知,答案是否定的,因为类型重叠意味着类型保护不会缩小类型。您可以在this playground中看到它。

1 个答案:

答案 0 :(得分:0)

与@ aluan-haddad进行讨论后,我得到了部分解决方案。我不能完全得到我想要的东西,即,“自动”类型定义可以区分具有所需参数的函数和不具有所需参数的函数,但是我想出了“区分器”函数,可以区分这两个函数并应用适当的品牌类型。

该解决方案依赖于使用条件类型,再加上单参数函数与零参数类型不兼容(即使反事实并非如此)的事实,以强制正确的类型。

type NoArg = {
    _brand: 'NoArg'
    ():void
}

type OneArg = {
  _brand: 'OneArg'
  (x:number): void
}

// Correct typings
let noArg: NoArg = discriminator(()=>{})
let oneArg: OneArg = discriminator((x:number)=>{})

// Both of these error, as hoped!
let noArgError: NoArg = discriminator((x:number)=>{})
let oneArgError: OneArg = discriminator(()=>{})

function discriminator<T extends (x:any)=>any>(myFunc: T) {
    let discriminatedFunc
    if(myFunc.length === 0) {
        discriminatedFunc = {
            _brand: 'NoArg',
            myFunc
        }
    } 
    else {
        discriminatedFunc = {
            _brand: 'OneArg',
            myFunc
        }
    }
    return discriminatedFunc as unknown as T extends ()=>any ? NoArg : OneArg
}

a playground上。

NB:就我而言,这是因为one参数函数与零类型函数不兼容,请注意条件类型T extends (x:number)=>any ? OneArg : NoArg的反向版本将不起作用,因为它始终解析为OneArg。

现在,这比首先通过构造函数对功能进行“品牌化”更好吗?我认为是这样,因为您只需要一个鉴别功能而不是两个商标功能。而且还因为您不必事先知道函数的签名,即将它们传递给鉴别器时就可以知道。

有人看到此解决方案有任何问题吗?有更好的一种,还是所有TS都允许?