在下面的代码中:
\ntype NoArg = {\n (): void\n}\n\ntype OneArg = {\n (x:number): void\n}\n\nlet noArg: NoArg = (x:number)=>{}\nlet oneArg: OneArg = ()=>{}\nRun Code Online (Sandbox Code Playgroud)\n只有第一个赋值才会产生编译器错误。我理解为什么会出现这种情况,因为 JavaScript 允许传递函数时使用少于其完整的可能参数集,并且 \xe2\x80\x99s 不同于说参数是可选的,这与函数的调用方式有关而不是它\xe2\x80\x99s 是如何通过的。请参阅常见问题解答。
\n但话虽如此,有没有办法构造一个与零参数函数不兼容的 OneArg 接口版本?
\n我知道这可以通过品牌或名义打字来完成,例如,
\ntype OneArg = {\n (x:number): void\n _brand: \xe2\x80\x9cOneArg\xe2\x80\x9d\n}\nRun Code Online (Sandbox Code Playgroud)\n但是,或者任何其他类型的名义类型解决方案,都需要在赋值时进行额外的工作(例如,您必须显式地将 _brand 属性添加到函数中)。
\n所以我的问题是\xe2\x80\x94是否有任何方法可以构造一个类型NoArg,该类型会使简单赋值失败let oneArg: OneArg = ()=>{}?
上面链接的常见问题解答说 \xe2\x80\x9cTypeScript 目前没有办法指示必须存在回调参数。\xe2\x80\x9c 这是否完全排除了我想要在这里执行的操作?我\xe2\x80\x99m 希望它不会\xe2\x80\x99t,因为这是\xe2\x80\x99t 回调参数,但也许原理是相同的。
\n更新:在下面的评论中,提出了是否可以通过类型保护测试来实现这一点的问题。据我所知,答案是否定的,因为类型重叠意味着类型保护不会缩小类型。你可以在这个操场上看到它。
\n经过与@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 允许的吗?
| 归档时间: |
|
| 查看次数: |
4712 次 |
| 最近记录: |