繁体   English   中英

嵌套函数的打字稿泛型推断

[英]Typescript generic infer of nested function

我有这些 ts 功能:

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

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

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

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

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

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

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

如果您查看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. paramI

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

当您尝试调用此函数时,问题就开始了。 为了调用fnGeneric ,你需要给出一个可以接受任何类型的函数,并将它变成你想要从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) ,您试图通过将fn强制为采用任何类型参数的函数,将number传递给采用string的函数。 这是一个类型检查的代码片段:

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 )。 它们都是完全等效的。

我可以给你更真实的场景,我制作了更简单的 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);

但调用它以:

“(value: number) => number”类型的参数不能分配给“(input?: U) => number”类型的参数。 参数“值”和“输入”的类型不兼容。 “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”类型的参数。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM