嵌套函数的Typescript通用推断

时间:2019-03-08 10:58:02

标签: typescript generics

我有以下ts函数:

const fnGeneric = <V,I>(fn:<U>(param:U) => V, param:I) => fn(param);
const fn = (some:string) =>  some;
const result = fnGeneric(fn,5);

但结果以静态类型错误结尾:

  

类型参数'(某些:字符串)=>字符串'不能分配给类型参数'(参数:U)=>字符串'。   参数类型'some'和'param'不兼容。   不能将'U'类型分配给'string'

此模式有什么问题?我认为您应该推断我输入的是数字,但是这里有一些空格。

3 个答案:

答案 0 :(得分:0)

我可以给您一个更现实的场景,我制作了fn的简化版本,这是真实的:

export const inject = <I,V>(fn:<U>(input?:U) => V, resolveWithPayload: boolean, resolveArgs?: I) => <R>(payload:R):R => { resolveWithPayload ? fn(payload) : resolveArgs ? fn(resolveArgs) : fn(); return payload; };

const fn = (value:number):number => {
    propertyToMutate = value;
    return propertyToMutate;
}

const res =_fish.inject(fn,false,60)(50);

但调用它以:

结尾
  

类型参数'(值:数字)=>数字'不能分配给类型参数'(输入?:U)=>数字'。   参数``值''和``输入''的类型不兼容。   不能将'U'类型分配给'数字'类型。

如果我通过您的方式改进代码:

export const inject = <I,V,U>(fn:(input?:U) => V, resolveWithPayload: boolean, resolveArgs?: I) => <R>(payload:R):R => { resolveWithPayload ? fn(payload) : resolveArgs ? fn(resolveArgs) : fn(); return payload; };

以类型定义错误结束,注入本身如下:

  

TS2345:类型'R'的参数不能分配给类型的参数   “ U”。

     

TS2345:类型'I'的参数不能分配给类型的参数   “ U”。

答案 1 :(得分:0)

我认为您的定义已关闭,不会自动推断出适合您意图的类型。这是我要使用的定义:

const fnGeneric = <V,U>(fn: (param:U) => V, param: U) => fn(param);
const fn = (some: string) =>  some;
const result = fnGeneric(fn, 5);

这在函数和第二个参数中使用相同的类型参数U,以确保它们的兼容性。当然,该调用将无效,因为fn仅接受字符串作为参数。

答案 2 :(得分:0)

如果您查看 fnGeneric 的类型签名,就像它出现在 .d.ts 文件中一样,其原因会变得更加清楚。

declare const fnGeneric: <V,I>(fn:<U>(param:U) => V, param:I) => V

UI 没有任何关系。以下是类型检查器可以从该签名推断出的内容:

  1. fnGeneric 是一个为所有类型定义的函数VI,接收2个参数fnparam和返回类型 V
  2. fn 是一个为所有类型定义的函数U接收1个参数param,类型为U并返回V
  3. param 的类型为 I

这种类型检查很好,因为 fn 是一个可以将任何类型转换为 V 的函数,因此在定义 I 中传递一个 fn(param) 是完美的可以接受。

当您尝试调用此函数时,问题就开始了。为了调用fnGeneric,你需要给出一个可以接受任何类型的函数,并将它变成你想从fnGeneric中取出的类型。这是不可能的!在您的示例中传递 const fn: (some: string) => string 不起作用,因为 fn 不接受任何类型,它只接受字符串。签名必须是 const fn: <U>(some: U) => string。因此错误:

<块引用>

'(some: string) => string' 类型的参数不能分配给 '(param: U) => string' 类型的参数。参数“some”和“param”的类型不兼容。类型 'U' 不能分配给类型 'string

此外,根据您的示例 fnGeneric(fn, 5),您正试图通过 强制 numberstring 传递给采用 fn成为一个接受任何类型参数的函数。这是一个类型检查的代码片段:

const fnGeneric = <A>(fn: <B>(param: B) => B, param: A): A => fn(param);
const fn = <A>(some: A): A => some;
const result: number = fnGeneric(fn, 5); // result = 5

fn 在这个例子中通常被称为 identity 函数 - 它返回给定的参数,它是具有签名的函数的唯一有效实现(没有任意类型转换) <A>(a: A) => A。通过扩展,fnGeneric 是一个函数,它接受恒等函数和任何类型的参数,并将恒等函数的应用返回给该参数。

<块引用>

这种模式有什么问题?

定义的

fnGeneric 没有任何意义,编写一个满足签名的类型检查的程序是不可能的。使类型工作的唯一方法只是使它变得多余。没有任何情况下最好调用 fnGeneric(identity, x) 而不是只调用 identity(x)(或者就此而言,只是表达式 x)。它们都是完全等效的。