在TypeScript中,如何编写回调可以位于多个参数位置的函数?
这是我到目前为止所做的,但是对于在TypeScript中,如何编写回调可以位于多个参数位置的函数?,typescript,Typescript,这是我到目前为止所做的,但是对于bar[key](请参阅)抛出了一个错误 我希望能够使用test(“hello”、{foo:bar}、()=>{})和test(“hello”、()=>{})调用test type TestCallback=()=>void 接口条{ [键:字符串]:字符串 } 常数测试=函数( 傅:字符串, bar?:bar | TestCallback | null, 回调?:TestCallback ) { if(条的类型==“函数”){ 回调=条 bar=null } i
bar[key]
(请参阅)抛出了一个错误
我希望能够使用test(“hello”、{foo:bar}、()=>{})和test(“hello”、()=>{})调用test
type TestCallback=()=>void
接口条{
[键:字符串]:字符串
}
常数测试=函数(
傅:字符串,
bar?:bar | TestCallback | null,
回调?:TestCallback
) {
if(条的类型==“函数”){
回调=条
bar=null
}
if(对象的条形图和条形图实例){
Object.keys(bar).forEach(函数(key){
console.log(键,条[key])
})
}
如果(回调){
回调函数()
}
}
您的问题与您使用bar
函数参数的方式有关,默认情况下TS将参数视为常量,但当我们更改参数时,您通过设置bar=null
将其视为let
。这种差异在下面可以看到:
{
// with let function declaration has not narrowed type
let a: string | number = 1;
if (typeof a === 'number') {
a // a is number yep
const f = () => {
a // a is not to be believed so it is string | number
}
}
}
{
// with const function declaration has narrowed type
const a: string | number = 1;
if (typeof a === 'number') {
a // a is number yep
const f = () => {
a // a is still number as it is const
}
}
}
因此,当我们在类型内使用const
函数声明时,guard会将此类型视为缩小,但使用let
时,它不信任它。为什么?因为例如,函数可以异步调用,变量可以在之前更改,所以TS对let
更具防御性,以防止运行时错误
这种情况适用于您的情况,通过重新分配参数bar
TS将其切换到let
模式。我们可以通过删除重新分配来修复它。考虑:
const test = function(
foo: string,
bar?: Bar | TestCallback,
callback?: TestCallback
) {
if (bar && typeof bar !== 'function') {
Object.keys(bar).forEach((key) => {
console.log(key, bar[key]) // no error bar is Bar
})
}
if (callback) {
callback()
}
}
其他想法
关于这种行为的一些想法,以及它是否是错误的。它的主观性,我们需要了解很多因素,这其中也有一个需要快速编译的因素。TS在这里选择的是直截了当的方法,你重新分配,TS对你的类型不太相信。在像您这样的情况下,这可能是一种负担,但仍有许多其他情况受益于这种安全性 您的问题与您使用bar
函数参数的方式有关,默认情况下TS将参数视为常量,但当我们更改参数时,您通过设置bar=null
将其视为let
。这种差异在下面可以看到:
{
// with let function declaration has not narrowed type
let a: string | number = 1;
if (typeof a === 'number') {
a // a is number yep
const f = () => {
a // a is not to be believed so it is string | number
}
}
}
{
// with const function declaration has narrowed type
const a: string | number = 1;
if (typeof a === 'number') {
a // a is number yep
const f = () => {
a // a is still number as it is const
}
}
}
因此,当我们在类型内使用const
函数声明时,guard会将此类型视为缩小,但使用let
时,它不信任它。为什么?因为例如,函数可以异步调用,变量可以在之前更改,所以TS对let
更具防御性,以防止运行时错误
这种情况适用于您的情况,通过重新分配参数bar
TS将其切换到let
模式。我们可以通过删除重新分配来修复它。考虑:
const test = function(
foo: string,
bar?: Bar | TestCallback,
callback?: TestCallback
) {
if (bar && typeof bar !== 'function') {
Object.keys(bar).forEach((key) => {
console.log(key, bar[key]) // no error bar is Bar
})
}
if (callback) {
callback()
}
}
其他想法
关于这种行为的一些想法,以及它是否是错误的。它的主观性,我们需要了解很多因素,这其中也有一个需要快速编译的因素。TS在这里选择的是直截了当的方法,你重新分配,TS对你的类型不太相信。在像您这样的情况下,这可能是一种负担,但仍有许多其他情况受益于这种安全性 这似乎是TypeScript编译器做出的一个奇怪的决定
bar
不是不可变的,因此它可以更改
您在另一个函数中定义了bar[key]
(对.forEach的回调)
)
编译器似乎认为,正因为如此,bar
在函数执行时可能已经发生了变化
这是不正确的-函数是同步定义和使用的,因此不可能在定义和调用之间重新分配bar
尽管如此,编译器仍在抱怨
这里有一些避免的方法
类型断言(不推荐)
只要否决编译器:
if (bar && bar instanceof Object) {
Object.keys(bar).forEach(function(key) {
console.log(key, (bar as Bar)[key])
})
}
这是最简单的一个,但如果您将bar
的类型更改为其他类型bar?:bar | Baz | TestCallback | null
,并且忘记更新类型断言,则可能会出现问题。编译器不会抱怨,因此很容易忽略它
重复类型防护装置(不推荐)
这还将确保编译器在使用bar
时,它实际上是bar
类型。然而,这是一个无用的检查,因为它不应该是必要的。这使得代码更难阅读和理解
使用const
不能重新分配const
,因此编译器将知道baz
只能是类型guards之后的Bar
,并且在函数执行时不会更改
仍然添加了额外的一行,但至少比直接推翻编译器或重新评估条的类型更明智
提取forEach
回调并将其咖喱化
这是一个选项,但可能不会每次都有用
const f = (obj: Bar) => (key: keyof Bar) => {
console.log(key, obj[key])
}
/* ... */
if (bar && bar instanceof Object) {
Object.keys(bar).forEach(f(bar))
}
这类似于使用const
,因为它将bar
捕获到编译器不会抱怨可能更改的不同变量中。另一方面,这可能是一个过度的任务。然而,如果您已经想要函数,它在某些情况下可能更有用
将用于…的
(推荐)
这仍然是一种绕过编译器错误解释的方法,只需消除错误解释的原因-删除函数定义:
if (bar && bar instanceof Object) {
for (let key of Object.keys(bar)) {
console.log(key, bar[key]);
}
}
如果没有函数定义,编译器只能解释在检查其类型和使用时间之间,bar
不会改变。这似乎是TypeScript编译器的一个奇怪决定
bar
不是不可变的,因此它可以更改
您在另一个函数中定义了bar[key]
(对.forEach的回调)
)
编译器似乎认为