TypeScript中的必选泛型类型和推断泛型类型

14
以下泛型类型有何区别:
type FnWithRequiredParam<T> = (t: T) => void
type FnWithParamInferred = <T>(t: T) => void

据我所了解,如果在任何情况下没有显式给出泛型类型,FnWithRequiredParam函数将始终失败。传递强制执行的泛型(例如FnWithRequiredParam<string>)将在所有上下文中基本上将其转换为(t: string) => void
然而,我找不到FnWithParamInferred的含义。在某些情况下,<T>从使用它的位置推断出来(例如Array.map),但以下行会抛出错误:
var f: FnWithParamInferred = (a: number) => { console.log(a) }

在上述语句中,说numberT不兼容。那么实际上T是什么?它从未被精确地声明,并且正在与另一种类型进行比较。在像<T>(...) => ...这样的函数类型中定义泛型T的规则是什么?
似乎,如果<T>被定义为类/接口的必需泛型,例如Array<T>,则数组的方法可以成功推断出T。但如果它在类/接口之外,则类型推断似乎无法正常工作。
1个回答

24

这两者在它们定义的函数签名方面非常不同。

  • 第一个定义了一个常规的函数签名,可以在使用时通过类型参数进行自定义,该类型将固定为签名。
  • 第二个定义了一个通用的函数签名,是一个可以接受任何类型T参数的函数,在调用函数时T被推断(或明确指定)。

考虑以下声明:

declare const fn: FnWithRequiredParam<number> 
declare const genericFn: FnWithParamInferred;

// T was fixed on declaration 
fn(1) // ok
fn("1") // err  

// T is decded by the caller
genericFn(1) // ok T is number for this call
genericFn("1") // ok T is string  for this call
genericFn<number>("1") // err T was specified as number but string was passed in 

你遇到错误的原因是你试图将一个带有“number”参数的函数分配给一个应该接受任何类型参数“T”的函数,其中“T”由函数调用者决定。只有一个通用函数才能满足类型“FnWithParamInferred”。
var f: FnWithParamInferred = <T>(a: T) => { console.log(a) }

我认为你真正想做的是在变量声明中省略显式类型参数,并让它根据分配给它的值进行推断。Typescript 不支持这一点。如果您为变量定义了类型注释,则不会对该变量进行推断。

您可以完全省略类型注释,以让编译器推断函数类型:

var f = (a: number) => { console.log(a) } // inferred as (a: number) => void

你可以定义一个通用的辅助函数来推断 T,但是基于 FnWithRequiredParam 限制函数签名。

function createFunction<T>(fn: FnWithRequiredParam<T>) {
    return fn;
}

var f = createFunction((a: number) => { console.log(a) }) // inferred as FnWithRequiredParam<number>

1
非常感谢您,Titian!那么,是否可以说我之前尝试通过将通用函数分配给特定类型的函数来错误地推断;而正确的推断方式不是通过分配,而是通过调用函数呢? - ducin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接