在 TypeScript 中设置所需的函数参数

sam*_*256 6 typescript

在下面的代码中:

\n
type NoArg = {\n  (): void\n}\n\ntype OneArg = {\n  (x:number): void\n}\n\nlet noArg: NoArg = (x:number)=>{}\nlet oneArg: OneArg = ()=>{}\n
Run Code Online (Sandbox Code Playgroud)\n

只有第一个赋值才会产生编译器错误。我理解为什么会出现这种情况,因为 JavaScript 允许传递函数时使用少于其完整的可能参数集,并且 \xe2\x80\x99s 不同于说参数是可选的,这与函数的调用方式有关而不是它\xe2\x80\x99s 是如何通过的。请参阅常见问题解答

\n

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

\n

我知道这可以通过品牌或名义打字来完成,例如,

\n
type OneArg = {\n  (x:number): void\n  _brand: \xe2\x80\x9cOneArg\xe2\x80\x9d\n}\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n

所以我的问题是\xe2\x80\x94是否有任何方法可以构造一个类型NoArg,该类型会使简单赋值失败let oneArg: OneArg = ()=>{}

\n

上面链接的常见问题解答说 \xe2\x80\x9cTypeScript 目前没有办法指示必须存在回调参数。\xe2\x80\x9c 这是否完全排除了我想要在这里执行的操作?我\xe2\x80\x99m 希望它不会\xe2\x80\x99t,因为这是\xe2\x80\x99t 回调参数,但也许原理是相同的。

\n

更新:在下面的评论中,提出了是否可以通过类型保护测试来实现这一点的问题。据我所知,答案是否定的,因为类型重叠意味着类型保护不会缩小类型。你可以在这个操场上看到它。

\n

sam*_*256 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
}
Run Code Online (Sandbox Code Playgroud)

还有操场上。

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

现在,这比首先通过构造函数“品牌化”函数更好吗?我认为是的,因为你只需要一个鉴别器函数而不是两个品牌函数。而且还因为您不一定需要事前知道函数的签名,即当您将它们传递给鉴别器时。

有人看到这个解决方案有什么问题吗?有没有更好的,或者这都是 TS 允许的吗?