高阶函数泛型类型的 TypeScript 默认函数

毛里西奥·罗巴约

我有一个高阶函数,它根据作为参数传递的泛型函数正确推断其返回函数的类型,用一个简单的例子可能更容易理解:

function someFunction<T>(f: (s: string) => T) {
  return function(s: string): T {
    return f(s)
  }
}

因此,如果我传递一个返回 astring的函数,则返回的函数将正确推断类型:

const funcStr = someFunction((s: string) => s)
//    ^ const funcStr: (s: string) => string

如果我传递一个number返回 a 的函数,它将返回一个函数,该函数又返回一个数字:

const funcNum = someFunction((s: string) => Number(s))
//    ^ const funcStr: (s: string) => number

现在我想为高阶函数添加一个默认值,但一直无法使其工作:

// Type '(s: string) => number' is not assignable to type '(s: string) => T'.
function someFunction<T>(f: (s: string) => T = (s: string) => Number(s)) {
//                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  return function(s: string): T {
    return f(s)
  }
}

如何将默认函数添加到高阶函数并在使用另一个函数时仍使其推断类型?

工作 TS 操场

杰卡兹

从技术上讲,编译器抱怨是正确的。您始终可以使用类型断言来抑制默认函数参数上的错误,并为其提供默认类型参数T以便编译器在无法T从参数中推断出时知道该怎么做

function someFunction<T = number>(
  f: (s: string) => T = ((s: string) => Number(s)) as any
) {
  return function (s: string): T {
    return f(s)
  }
}

这将完全如您所愿:

const fNum = someFunction()
console.log(fNum("3.14159").toFixed(2)); // 3.14
const fStr = someFunction((s: string) => s)

但是这种方法的问题是类型参数T是由 的调用者指定someFunction(),而不是由实现指定的。所以没有什么能阻止恶意和/或困惑的调用者做这样的事情:

const fOops = someFunction<string>();

调用者已指定T为 a string,即使T的默认值为number,它也已被显式string类型覆盖所以编译器认为它fOops是 type (s: string) => string,但当然在运行时我们知道它不会是:

try {
  fOops("oopsie").toUpperCase(); // no compiler error
} catch (e) {
  console.log(e); // fOops(...).toUpperCase is not a function
}

这就是编译器抱怨的原因;这是说它不能确定默认值f将是适合T调用者指定的类型

如果您不关心这种可能性,那么这种方法很好,并且需要对您的代码进行最少的更改。


如果你想阻止的可能性fOops样的情况,你可以改变someFunction()成为一个重载函数有两个呼叫签名:

// call signatures
function someFunction(): (s: string) => number;
function someFunction<T>(f: (s: string) => T): (s: string) => T;

如果不someFunction()带参数调用,则调用的是第一个调用签名,并且返回类型必须是(s: string) => number; 此调用签名上根本没有泛型类型参数。

否则,如果您使用回调参数调用它,那么您将调用第二个调用签名;有一个类型参数T对应于该回调的返回类型。

然后实现可以与之前类似,但您可以放松类型以防止投诉(这里我们将仅用any作返回​​类型):

function someFunction(f: (s: string) => any = (s: string) => Number(s)) {
  return function (s: string) {
    return f(s)
  }
}

现在您打算支持的呼叫仍然有效:

const fNum = someFunction()
console.log(fNum("3.14159").toFixed(2)); // 3.14

const fStr = someFunction((s: string) => s)

但是不可能使用类型参数调用零参数调用签名,因此您不能再以错误的方式调用它:

const fOops = someFunction<string>() // error!
// Expected 1 arguments, but got 0.

Playground 链接到代码

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

泛型类型的TypeScript函数重载

TypeScript 类型获取约束函数类型泛型的返回类型

Typescript泛型:从函数参数的类型推断类型?

TypeScript:通过扩展泛型自动返回类型的函数类型

高阶函数中的打字稿泛型类型推断

Typescript 默认函数参数

使用 typescript 泛型来推断 param 函数的类型

TypeScript泛型:从函数参数推断键值返回类型的键

在TypeScript中调用泛型类型的构造函数

函数中的泛型不检查Typescript中的参数类型

使用泛型和构造函数的 TypeScript 类型转换?

从TypeScript中的泛型类型获取构造函数/实例

具有泛型的TypeScript接口函数类型

如何在 Typescript 函数接口中约束泛型类型?

Typescript从具有泛型类型的对象索引调用函数

TypeScript 中泛型类型的返回构造函数

TypeScript泛型函数的类型

如何在 TypeScript 中为泛型类型编写泛型辅助函数?

Typescript中的默认泛型类型派生

具有高阶函数的泛型

打字稿:如何从返回函数的输入参数推断高阶函数中的泛型类型

Typescript推断高阶函数中内部函数的类型

反应:TypeScript泛型,自动从函数切换参数类型(作为道具的函数)

Typescript泛型包装器:未类型化的函数调用可能不接受类型参数

TypeScript泛型和函数参数

TypeScript泛型-回调函数推论

嵌套TypeScript函数中的泛型

Typescript:具有泛型类型的函数在返回时是否在泛型对象中强制执行?

TypeScript泛型函数返回类型问题