首页 > 解决方案 > 如何编写回调可以在 TypeScript 中多个参数位置的函数?

问题描述

这是我到目前为止所拥有的,但是会引发错误bar[key](请参阅TypeScript Playground)。

我希望能够test使用test("hello", { foo: "bar" }, () => {})and调用test("hello", () => {})

type TestCallback = () => void

interface Bar {
  [key: string]: string
}

const test = function(
  foo: string,
  bar?: Bar | TestCallback | null,
  callback?: TestCallback
) {
  if (typeof bar === "function") {
    callback = bar
    bar = null
  }
  if (bar && bar instanceof Object) {
    Object.keys(bar).forEach(function(key) {
      console.log(key, bar[key])
    })
  }
  if (callback) {
    callback()
  }
}

标签: typescript

解决方案


您的问题与您使用bar函数参数的方式有关,默认情况下 TS 将参数视为 consts,但是当我们更改它们时,您通过设置bar = null然后 TS 将其视为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在类型保护中使用函数声明时,会将此类型视为缩小范围,但let不信任它。为什么?因为例如函数可以异步调用并且变量可以在之前更改,所以 TSlet为了防止运行时错误而更具防御性。

这种情况适用于您的情况,通过重新分配参数barTS 将其切换到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 不太相信你的类型守卫持久性。在像您这样的某些情况下,这可能是一种负担,但仍有很多其他情况可以从这种安全性中受益。


推荐阅读